9.5 The MCP Protocol: A Bridge Between AI and IoT
9.5.1 Background and Core Design of the MCP Protocol
The communication models of MQTT, CoAP, LwM2M, and OPC UA are essentially static: the platform defines the rules, data flows along topics or resource paths, and state changes are triggered by the device or the platform side. When AI applications operate these devices, the problem they face is no longer unreachable data, but three deeper gaps.
The semantic gap. MQTT publishes to a topic such as topic/dev/001/temp with a payload of 26.8. The AI can receive this value, but it cannot tell whether it is Celsius or Fahrenheit, an instantaneous value or a five-minute average, a normal range or an anomaly alarm. CoAP's path structure is somewhat more standardized, but the meaning of the fields still depends on the thing-model mapping on the platform side. What an AI system needs is not only a data stream but also a meta-description of device capabilities: which parameters are readable, which are writable, what constraints a write operation carries, and how return values should be interpreted.
Missing security boundaries and context. An AI application that subscribes to device topics directly through an MQTT client either gains too many privileges (it can read other tenants' devices) or lacks the context for control operations (it does not know whether the target device is in an operable state). An MQTT broker does not maintain session state, authorization context, or a call chain for an AI conversation. Processing one question usually requires multi-step reasoning that touches multiple devices or data sources, and every call must carry the security context already established.
Asymmetric state management. Device communication protocols are mostly event-driven or polling models: the device reports, and the platform consumes. An AI Agent's task, however, usually spans multiple steps: it first understands the current state, then decides the next action, and finally confirms the result. MQTT's publish–subscribe model is not well suited to query–response patterns; CoAP's request–response model is closer, but it has no unified mechanism for tool discovery and parameter description. AI needs an interaction protocol with discoverable capability boundaries. Task state, conversational memory, and approval progress must be maintained by the Host, Agent Runtime, or business system, rather than assuming that the base protocol stores this state for the application.
MCP (Model Context Protocol) emerged against exactly this background. It is not a device protocol; it is a context-exchange protocol for interactions between AI applications and external tools, resources, and knowledge bases.
Before going further, it helps to separate two kinds of statements. Facts about the IoT DC3 implementation are tied to source snapshot 987c96d50 dated August 29, 2026. The application and value boundaries of this class of protocols in IoT are the author's engineering judgment, not a final definition of the formal standard, and some details are illustrative. After a version upgrade, the endpoint, protocol revision, declared capabilities, and authorization path must be checked again.
MCP's context model and communication model
MCP abstracts interaction between AI and external systems as discoverable capabilities and structured requests. The specification defines three core capability categories — resources, tools, and prompts — but a particular server may implement only a subset:
- Resources: readable context exposed by the server for a Host or Client to include in model context as needed. Resources are identified by URI and may carry a MIME type; the specification also provides resource templates, list pagination, and optional subscriptions. That is not the same as HTTP content negotiation or arbitrary byte-range reads.
- Tools: executable actions that can be triggered by a model request. Each tool declares an input schema describing parameter names, types, constraints, and whether they are required. The AI model proposes a call, while the MCP Server still has to enforce authorization, parameter validation, risk controls, and auditing. In this IoT DC3 source snapshot, the server declares only the Tools capability. It combines the platform catalog in
dc3_apianddc3_resourcewith versioned staticopenapi-*.jsonsnapshots, then trims the resulting tool definitions by OAuth scope, tenant, permission, and risk policy. This is not unbounded runtime crawling of every center's OpenAPI, nor does it imply that Resources or Prompts are implemented. - Prompts: reusable, parameterized prompt templates that let the server guide the model on "how to understand this domain's resources."
IoT DC3's MCP endpoint exchanges messages over JSON-RPC 2.0. This source snapshot implements the 2025-06-18 initialization handshake and handles initialize, notifications/initialized, ping, tools/list, and tools/call. The Gateway exposes POST /mcp and introspects the Bearer Token on every request. The current code does not declare Resources, Prompts, or Tasks. A JSON-RPC request ID only correlates a request with its response, and initialization state does not mean that the server stores a conversational session. Cross-call task state, timeout compensation, and approval records must still reside in the Agent Runtime or business storage. Authentication also depends on the transport and deployment model. OAuth 2.1 is the authorization foundation here and, as of August 2026, remains an IETF draft rather than a published RFC.
Figure 9-9 shows a typical MCP interaction sequence in an IoT scenario, covering initialization, Tool-catalog discovery, Tool invocation, and state feedback.
This design differs from MQTT's Topic-based publish/subscribe model. In the 2025-06-18 lifecycle implemented by IoT DC3, MCP completes initialization and capability negotiation, then discovers and invokes capabilities through structured requests. The server trims the Tool catalog using identity, tenant, and policy context revalidated on each request. Such protocol-handshake state is not business conversation or task state; cross-call state still belongs to the Host, Agent Runtime, or business storage. The July 28, 2026 release candidate proposes a stateless lifecycle without initialize, carrying protocol metadata in requests; that proposal must not be projected backward onto this source snapshot.
The division of labor between MCP and the IoT platform
In IoT DC3, the MCP entry point sits in the Gateway, the Tool catalog and policy are managed through Auth Center capabilities, and execution is routed to the selected platform-center API. It is a platform adaptation entry point, not a replacement for device-side protocols. In this source snapshot, the call chain is:
- An AI Agent, such as Claude Desktop or a custom Agent, completes initialization and obtains through
tools/listthe Tool catalog visible to the current Bearer Token, tenant, and permission context. - The MCP Gateway derives candidate tools from the API/resource catalog and versioned OpenAPI snapshots, then applies scope, tenant, permission, and risk filtering before returning them. On
tools/call, it revalidates visibility and authorization instead of trusting the previously returned catalog alone. - When the Agent invokes a "read device point" tool, the Gateway reads data or triggers the existing command path through a controlled platform-center API. It neither sends CoAP directly to the device nor publishes directly to a device Topic.
This design ensures that MCP does not bypass the existing IoT security governance. The device-side protocols remain MQTT, CoAP, OPC UA, or Modbus. What MCP adds is an interoperability layer between AI and the platform, not a reinvention of device communication protocols.
Pitfalls to avoid in engineering practice
In practice, teams are tempted to treat MCP as a shortcut for "letting AI connect directly to devices." The most typical design mistake is an MCP Server that maintains its own MQTT connection pool and publishes directly to device topics whenever the agent invokes a tool. Such an architecture bypasses the platform layer's policy engine, service degradation, tenant isolation, and interlocking logic, and hands the duties of two-factor confirmation, write-rate limiting, and operation audit over to the AI prompt. An AI model is not a deterministic real-time control system; any call chain that bypasses platform governance should be treated as a security violation.
The sounder judgment is this: the correct place for MCP in IoT is the interoperability layer between AI applications and the IoT platform. It answers "how does AI discover and invoke platform capabilities through a unified protocol," not "how does AI replace MQTT/CoAP and take over device communication." The platform still receives telemetry over MQTT, manages devices over CoAP, and carries industrial semantics over OPC UA; MCP only adds an AI-facing tool abstraction that lets the model operate policy-trimmed platform capabilities inside a security context. These two stacks should never be short-circuited directly, unless the architect is willing to accept open-loop control risk.
Further reading: Chapter 7, Section 7.3, covers the Tool catalog, platform conversation state, and security policy in the IoT DC3 Agentic Center. MCP protocol-handshake state is not platform conversation state, nor does it preserve business tasks for the application. Chapter 8, Section 8.5.4, discusses the security boundaries and auditing scheme for AI Agents operating devices.
9.5.2 MCP Message Format and Capability Description
MCP uses JSON-RPC 2.0 as its message carrier. The choice does not minimize payload size; it lowers the entry barrier for AI applications because languages with JSON serialization can process the messages directly. The published 2025-11-25 specification defines stdio and Streamable HTTP, with Streamable HTTP replacing the earlier HTTP+SSE transport. The IoT DC3 source snapshot contains one POST /mcp endpoint that handles JSON-RPC. What can be confirmed is therefore an HTTP POST MCP endpoint; its path alone does not prove implementation of every Streamable HTTP GET, SSE, and session semantic. Experimental Tasks appeared in the 2025-11-25 specification, but this snapshot does not implement them. The 2026-07-28 document is a release candidate proposing changes such as a stateless lifecycle, not a stable implementation baseline. Chapter 7, Section 7.1.5 discusses these mechanisms from the standpoint of IoT DC3's implementation boundary; this section focuses on protocol layers and version boundaries. MCP does not define device-side envelopes, frame headers, or payload formats. It addresses how an AI application discovers and invokes external capabilities and how those capabilities describe themselves.
Standard Message Model and Capability Negotiation
This section first describes the 2025-06-18 lifecycle implemented by IoT DC3; the published 2025-11-25 specification also retains this handshake. The Client first sends initialize with its protocol revision and capabilities. The Server returns the selected revision, capabilities, and implementation information, after which the Client sends notifications/initialized. Subsequent operations must conform to the negotiated result. Initialization state constrains protocol interaction; it does not mean that the Server stores business conversations, approvals, or long-running task state. A client targeting the July 28, 2026 release candidate must instead follow its stateless lifecycle rather than mixing the two flows.
An initialize request looks like this:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "iot-supervisor-agent",
"version": "1.0.0"
}
}
}This example uses 2025-06-18, the revision declared by the IoT DC3 source snapshot. A Client should send a revision it supports and handle the version selected by the Server, rather than using an ambiguous value such as v1. Capability negotiation is not an authorization credential. Authentication, authorization, and capabilities should be revalidated for a new connection or interaction context. The Server may have changed its Tools or resource paths, and a Client should not reuse a stale catalog indefinitely across contexts.
The specification allows a Server to declare the capabilities it actually supports. Common categories include:
- tools: actions the model may invoke; each must declare a name, a description, and JSON Schema input parameters.
- resources: context resources read by the client (device descriptions, historical summaries, documentation), supporting URI pattern matching.
- prompts: discoverable, parameterizable prompt templates used to steer model behavior.
A server need not support all three categories. This IoT DC3 snapshot declares only tools, so a client cannot infer from the general specification that resources/list or prompts/list is available.
JSON Schema is explicitly used for Tool input parameters. Resources are described through fields such as URIs, content, and templates, while Prompts have their own parameter and message structures; the three capability categories therefore must not be described as sharing one JSON Schema format. MCP does not define device-domain semantics for the platform. A Tool description for reading a device point looks like this:
{
"name": "iot_read_point",
"description": "Read the device points the current user has permission to access",
"inputSchema": {
"type": "object",
"properties": {
"deviceId": {"type": "string", "description": "Device identifier"},
"pointId": {"type": "string", "description": "Point identifier"}
},
"required": ["deviceId", "pointId"]
}
}The inputSchema here defines the invocation parameters of an MCP tool — not a device register mapping or a unified CoAP resource format. How the MCP server internally routes these parameters to the IoT platform's actual protocol driver is completely transparent to the AI application. The caller cares only about the name and the arguments, not whether the target device is reached over MQTT or Modbus.
The AI agent sends the actual operation request through the tools/call method:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "iot_read_point",
"arguments": {
"deviceId": "pump-001",
"pointId": "motor_temp"
}
}
}The server returns the result:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{"type": "text", "text": "motor_temp = 68.5°C"}],
"isError": false
}
}Along the entire call chain, the server is responsible for verifying user permissions, tenant boundaries, and data masking; the model never touches raw point values. MCP's "capability description" is in essence the interface contract of a security proxy, not the device's own feature list. This point matters especially to architects: if you want the model to manipulate device registers directly, that is a dangerous design that bypasses platform governance, and it should not be implemented through MCP.
The Responsibility Boundary of Capability Description: Differences from WoT TD
Device attributes, events, commands, data types, units, and protocol bindings should remain the responsibility of the thing model, LwM2M objects, the OPC UA information model, or the W3C Web of Things Thing Description. An MCP server can adapt these models to generate tools or resources, but that adaptation is not part of the MCP standard — MCP specifies only the description format of tools; it does not specify the measurement unit, enumeration range, or lifecycle of a "temperature attribute."
Take WoT TD as an example: the TD of a lighting device describes the brightness property, the setBrightness action, and its parameter constraints. An MCP server can generate a set_brightness tool from that TD, but it must additionally supply three things:
- User permissions — whether the current principal is authorized to invoke the action.
- Action risk level — whether the parameter-write operation requires a second confirmation.
- Idempotency policy — whether repeated invocation is safe.
The adaptation chain is as follows:
Device model / WoT TD / OPC UA information model
↓ adaptation and permission trimming
MCP tools / resources
↓
AI applications discover, interpret, and invokeThe JSON fields of a WoT TD cannot be used directly as MCP "capability description" fields. MCP cares only about the semantics of the calling interface; it does not define the units, enumerations, or lifecycle of device attributes. This is consistent with the layered semantic model discussed in Section 9.6.2 of this chapter: the bottom layer is the device standard model, the middle layer is the platform's internal adaptation, and the top layer is the interface discovered on the AI side.
The Complementary Relationship Between A2A and MCP
MCP solves the connection between AI applications and tools/resources. A2A solves discovery, task delegation, and result exchange between agents. The division of labor is clear: an orchestrating agent can delegate a "diagnose pump anomaly" task to a diagnostic agent through A2A, and the latter then queries device status and history through MCP.
Identity authentication, authorization, and user consent must not be bypassed by MCP or A2A. Every tool call must still verify the principal, the tenant, the action, and the parameters. Tool descriptions themselves are untrusted input — clients should restrict server sources and review changes to tool names and schemas, to keep malicious descriptions from inducing the model to leak context or invoke unauthorized capabilities. This aligns with the security checklist in Section 9.7.2 of this chapter: the protocol itself is not responsible for trust; trust is enforced by the platform layer's authorization and governance.
Boundary Judgments in Protocol Design
If a project needs a protocol for device registration, capability-catalog synchronization, or action execution, it can be designed as a platform-internal "device semantic adaptation protocol," defined independently of MCP. Such a protocol can run over MQTT, CoAP, or a message queue, but the following points must be made explicit:
- Message fields, registration flows, and error codes are custom content.
- Its relationship to MCP is adaptation or bridging, not part of the MCP specification.
- All hypothetical fields and example parameters should be labeled as such, so that readers do not mistake them for standardized definitions.
Real-time telemetry, device shadow synchronization, and safety control should prefer the IoT platform's existing data plane and control plane. MCP serves only as the capability-discovery and invocation entry point on the AI application side; it does not replace the device-side protocol stack. This boundary judgment is the engineering baseline an IoT architect must hold when introducing an AI interaction layer.
9.5.3 An MCP Engineering Prototype: AI-Controlled Lighting
Sections 9.5.1 and 9.5.2 covered MCP's design motivation and message format; this section strings them together through one complete scenario, showing how MCP (Model Context Protocol) links AI applications to the control chain of IoT devices.
Scenario: the user says to an AI voice assistant, "Set the bedroom light to warm yellow, brightness sixty percent." After natural-language parsing, tool discovery, parameter mapping, remote invocation, and state synchronization, the AI agent takes control of the smart light. Throughout the interaction, the AI agent never communicates with the device or the MQTT broker directly — it interacts only with the MCP Server; the MCP Server translates the tool call into the IoT platform's REST interface, and the platform issues the command over MQTT.
Device Registration and Capability Exposure
In this teaching prototype, the smart light declares set_light (set brightness and color) and get_status (query current state) when it registers with the IoT platform, and the adaptation layer maps controlled platform APIs into MCP Tools. This illustrates the layering relationship rather than reproducing IoT DC3's current Tool aggregator line for line. After initialize and notifications/initialized complete, the Client sends a separate tools/list request and the Server returns the visible Tools. The following is a simplified response fragment:
{
"tools": [
{
"name": "iot_get_device_status",
"description": "Query the device's current state, including brightness and color",
"inputSchema": {
"type": "object",
"properties": {
"deviceId": {"type": "string", "description": "Device ID"}
},
"required": ["deviceId"]
}
},
{
"name": "iot_set_light",
"description": "Set lamp brightness (0-100) and color (supports 'cool white', 'natural white', 'warm yellow', 'warm white')",
"inputSchema": {
"type": "object",
"properties": {
"deviceId": {"type": "string"},
"brightness": {"type": "integer", "minimum": 0, "maximum": 100},
"color": {"type": "string", "enum": ["cool white", "natural white", "warm yellow", "warm white"]}
},
"required": ["deviceId", "brightness"]
}
}
]
}AI Parsing and Tool Invocation
The AI agent parses the user's speech into a tool-call intent. This process usually involves named-entity recognition ("bedroom light" → device ID light-bedroom-01), parameter extraction ("sixty" → 60, "warm yellow" → the corresponding color enum value), and tool matching (selecting iot_set_light). The agent then constructs a tools/call request:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "iot_set_light",
"arguments": {
"deviceId": "light-bedroom-01",
"brightness": 60,
"color": "warm yellow"
}
}
}When the MCP Server receives the request, it calls the IoT platform API through internal handlers, and the platform performs the real operation through its existing command path. The Python code below simulates the message flow from Agent to Server to platform and device. It omits the HTTP transport wrapper, MCP initialization handshake, request authentication, and external task state, focusing only on core message handling and state changes. The request_context in the code is illustrative business authorization context, not a conversational session stored by the MCP Server:
import json
import time
from dataclasses import dataclass, field
# ---------- Device abstraction in the simulated IoT platform ----------
@dataclass
class LightDevice:
device_id: str
brightness: int = 0
color: str = "cool white"
online: bool = True
def set_light(self, brightness: int, color: str) -> bool:
if not self.online:
raise RuntimeError("device offline")
if not (0 <= brightness <= 100):
raise ValueError("brightness out of range")
if color not in ["cool white", "natural white", "warm yellow", "warm white"]:
raise ValueError("unsupported color")
self.brightness = brightness
self.color = color
return True
# ---------- Simulated MCP Server ----------
class MCPToolServer:
def __init__(self, platform):
self.platform = platform
self.tools = {
"iot_get_device_status": {"handler": self.handle_get_status},
"iot_set_light": {"handler": self.handle_set_light}
}
def handle_get_status(self, request_context, args):
device = self.platform.get_device(args["deviceId"])
if device is None:
return {"error": "device not found"}
return {
"brightness": device.brightness,
"color": device.color,
"online": device.online
}
def handle_set_light(self, request_context, args):
device = self.platform.get_device(args["deviceId"])
if device is None:
return {"error": "device not found"}
try:
device.set_light(args.get("brightness"), args.get("color", "cool white"))
# The platform issues the real command over MQTT
mqtt_publish(device.device_id, device.brightness, device.color)
return {
"success": True,
"state": {
"brightness": device.brightness,
"color": device.color
}
}
except (ValueError, RuntimeError) as e:
return {"error": str(e)}
# ---------- Simulated MQTT publish ----------
def mqtt_publish(device_id, brightness, color):
print(f"[MQTT] Command issued: {device_id} brightness={brightness} color={color}")
# ---------- Simulated IoT platform ----------
class IoTPlatform:
def __init__(self):
self.devices = {}
def register_device(self, device: LightDevice):
self.devices[device.device_id] = device
def get_device(self, device_id):
return self.devices.get(device_id)
# ---------- Simulated AI Agent (MCP Client) ----------
class AIAgent:
def __init__(self, mcp_server: MCPToolServer):
self.server = mcp_server
self.request_context = {"user": "admin"}
def parse_intent(self, text: str):
"""Simplified intent parsing, for demonstration only"""
if "bedroom light" in text and "brightness" in text:
brightness = 60 if ("sixty" in text or "60" in text) else 50
color = "warm yellow" if "warm yellow" in text else "cool white"
return "iot_set_light", {
"deviceId": "light-bedroom-01",
"brightness": brightness,
"color": color
}
return None, None
def execute_intent(self, tool_name, args):
if tool_name not in self.server.tools:
print("Tool not found")
return
result = self.server.tools[tool_name]["handler"](self.request_context, args)
print(f"[AI Agent] Execution result: {result}")
return result
# ---------- Main flow ----------
def main():
platform = IoTPlatform()
device = LightDevice(
device_id="light-bedroom-01",
brightness=50,
color="cool white",
online=True
)
platform.register_device(device)
mcp_server = MCPToolServer(platform)
agent = AIAgent(mcp_server)
user_voice = "Turn the bedroom light to warm yellow, brightness sixty percent"
tool_name, args = agent.parse_intent(user_voice)
if not tool_name:
print("Unable to parse intent")
return
print(f"[Parse result] Tool: {tool_name}, Args: {args}")
result = agent.execute_intent(tool_name, args)
time.sleep(0.1)
print(f"[Final state] brightness={device.brightness}, color={device.color}")
if __name__ == "__main__":
main()Program output
[Parse result] Tool: iot_set_light, Args: {'deviceId': 'light-bedroom-01', 'brightness': 60, 'color': 'warm yellow'}
[MQTT] Command issued: light-bedroom-01 brightness=60 color=warm yellow
[AI Agent] Execution result: {'success': True, 'state': {'brightness': 60, 'color': 'warm yellow'}}
[Final state] brightness=60, color=warm yellowException Handling and Engineering Boundaries
In real deployments, the MCP Server must handle the following exception scenarios, returning structured error messages instead of crashing outright:
- Device offline: the platform detects that the device is unreachable and returns
{"error": "device offline"}. - Parameter out of range: the server validates and returns
{"error": "brightness out of range"}. - Insufficient permissions: the user in the current request context has no right to control the device; the Server should refuse the call and write an audit log.
- Timeout and retry: if no device acknowledgment arrives after the platform issues a command, decide whether to query state, compensate, or retry a limited number of times according to the action's semantics. A business wrapper may add
idempotencyKey, but it is not a standard field in the core MCPtools/call; both parties must define it explicitly in the Tool's input contract.
The core layering logic of this engineering pattern is that the AI agent never touches the device chain. Device registration, capability description, command execution, and state synchronization are still performed by the IoT platform and its existing protocols (such as MQTT); the MCP Server only performs translation and control duties between AI and the platform. This layering provides well-defined enforcement points for security audit, permission control, and tool version management, and it greatly reduces the awareness cost of device-side protocols when AI applications are integrated.