Skip to content

Commit cf2be0f

Browse files
authored
docs: add comprehensive docstrings to mcp_adapter.py classes (#324)
Add Google-style docstrings with Args, Returns, Raises, Attributes, and Example sections to MCPMessageType, MCPAdapter, and MCPServer classes. Also enhances docstrings for key methods including handle_message, _handle_tools_call, _handle_resources_read, and _map_tool_to_action. Fixes #316
1 parent 9921058 commit cf2be0f

File tree

1 file changed

+164
-30
lines changed
  • packages/agent-os/modules/control-plane/src/agent_control_plane

1 file changed

+164
-30
lines changed

packages/agent-os/modules/control-plane/src/agent_control_plane/mcp_adapter.py

Lines changed: 164 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,26 @@
4545

4646

4747
class MCPMessageType(Enum):
48-
"""MCP protocol message types"""
48+
"""MCP protocol message types.
49+
50+
Enumerates the JSON-RPC methods defined by the Model Context Protocol
51+
specification. Each value corresponds to a method string sent in the
52+
``"method"`` field of an MCP JSON-RPC 2.0 request.
53+
54+
Attributes:
55+
TOOLS_LIST: List available tools on the server.
56+
TOOLS_CALL: Invoke a registered tool by name.
57+
RESOURCES_LIST: List available resources (files, databases, etc.).
58+
RESOURCES_READ: Read the contents of a specific resource URI.
59+
PROMPTS_LIST: List available prompt templates.
60+
PROMPTS_GET: Retrieve a specific prompt template by name.
61+
COMPLETION: Request a completion (reserved for future use).
62+
63+
Example:
64+
>>> msg_type = MCPMessageType.TOOLS_CALL
65+
>>> msg_type.value
66+
'tools/call'
67+
"""
4968
TOOLS_LIST = "tools/list"
5069
TOOLS_CALL = "tools/call"
5170
RESOURCES_LIST = "resources/list"
@@ -75,19 +94,60 @@ class MCPMessageType(Enum):
7594

7695

7796
class MCPAdapter:
78-
"""
79-
MCP Protocol Adapter with Agent Control Plane Governance.
80-
81-
This adapter intercepts MCP protocol messages and applies governance
82-
rules before forwarding to the actual MCP server or client.
83-
84-
MCP uses JSON-RPC 2.0 for communication, with specific methods like:
85-
- tools/list: List available tools
86-
- tools/call: Execute a tool
87-
- resources/list: List available resources
88-
- resources/read: Read a resource
89-
90-
The adapter ensures all operations respect agent permissions.
97+
"""MCP Protocol Adapter with Agent Control Plane Governance.
98+
99+
Intercepts MCP protocol messages and applies governance rules before
100+
forwarding to the actual MCP server or client. MCP uses JSON-RPC 2.0
101+
for communication, with specific methods like:
102+
103+
- ``tools/list``: List available tools
104+
- ``tools/call``: Execute a tool
105+
- ``resources/list``: List available resources
106+
- ``resources/read``: Read a resource
107+
108+
The adapter ensures all operations respect agent permissions and
109+
policies defined in the control plane. Unknown tools are denied by
110+
default (secure-by-default).
111+
112+
Args:
113+
control_plane: The ``AgentControlPlane`` instance for governance.
114+
agent_context: The ``AgentContext`` for the agent using this adapter.
115+
mcp_handler: Optional upstream MCP message handler to delegate to
116+
after governance checks pass.
117+
tool_mapping: Optional custom mapping from tool names to
118+
``ActionType`` values. Merged with ``DEFAULT_MCP_MAPPING``.
119+
on_block: Optional callback invoked when an action is blocked.
120+
Receives ``(tool_name, arguments, check_result)``.
121+
logger: Optional logger instance.
122+
123+
Attributes:
124+
registered_tools: Dictionary of tool name to tool metadata.
125+
registered_resources: Dictionary of URI pattern to resource metadata.
126+
tool_mapping: Combined mapping of tool/operation names to
127+
``ActionType`` values used for governance decisions.
128+
129+
Example:
130+
>>> from agent_control_plane import AgentControlPlane
131+
>>> from agent_control_plane.mcp_adapter import MCPAdapter
132+
>>>
133+
>>> cp = AgentControlPlane()
134+
>>> ctx = cp.create_agent("my-agent")
135+
>>> adapter = MCPAdapter(control_plane=cp, agent_context=ctx)
136+
>>>
137+
>>> # Register a tool
138+
>>> adapter.register_tool("read_file", {
139+
... "name": "read_file",
140+
... "description": "Read a file from disk",
141+
... "inputSchema": {"type": "object", "properties": {"path": {"type": "string"}}}
142+
... })
143+
>>>
144+
>>> # Handle an MCP request — governance is applied automatically
145+
>>> response = adapter.handle_message({
146+
... "jsonrpc": "2.0",
147+
... "id": 1,
148+
... "method": "tools/call",
149+
... "params": {"name": "read_file", "arguments": {"path": "/tmp/data.txt"}}
150+
... })
91151
"""
92152

93153
def __init__(
@@ -130,17 +190,24 @@ def __init__(
130190
)
131191

132192
def handle_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
133-
"""
134-
Handle an MCP protocol message with governance.
135-
193+
"""Handle an MCP protocol message with governance.
194+
136195
This is the main entry point for MCP messages. It parses the
137-
JSON-RPC message, applies governance, and returns the result.
138-
196+
JSON-RPC message, applies governance checks via the control plane,
197+
and returns the result.
198+
139199
Args:
140-
message: MCP JSON-RPC 2.0 message
141-
200+
message: MCP JSON-RPC 2.0 message containing ``jsonrpc``,
201+
``method``, ``params``, and ``id`` fields.
202+
142203
Returns:
143-
JSON-RPC 2.0 response
204+
A JSON-RPC 2.0 response dict. On success, contains a
205+
``"result"`` key. On failure (governance block or error),
206+
contains an ``"error"`` key with ``"code"`` and ``"message"``.
207+
208+
Raises:
209+
PermissionError: Internally raised when governance blocks an
210+
action; caught and converted to a JSON-RPC error response.
144211
"""
145212
# Parse the JSON-RPC message
146213
jsonrpc = message.get("jsonrpc", "2.0")
@@ -198,7 +265,22 @@ def _handle_tools_list(self, params: Dict) -> Dict:
198265
return {"tools": allowed_tools}
199266

200267
def _handle_tools_call(self, params: Dict) -> Dict:
201-
"""Handle tools/call - execute a tool with governance."""
268+
"""Handle tools/call — execute a tool with governance.
269+
270+
Maps the tool name to an ``ActionType``, checks permissions via
271+
the control plane, and either delegates to the registered handler
272+
or returns the control plane result.
273+
274+
Args:
275+
params: JSON-RPC params containing ``"name"`` and ``"arguments"``.
276+
277+
Returns:
278+
Tool execution result dict with MCP ``content`` format.
279+
280+
Raises:
281+
PermissionError: If the tool is unknown or governance denies
282+
the action.
283+
"""
202284
tool_name = params.get("name", "")
203285
arguments = params.get("arguments", {})
204286

@@ -259,7 +341,20 @@ def _handle_resources_list(self, params: Dict) -> Dict:
259341
return {"resources": allowed_resources}
260342

261343
def _handle_resources_read(self, params: Dict) -> Dict:
262-
"""Handle resources/read - read a resource with governance."""
344+
"""Handle resources/read — read a resource with governance.
345+
346+
Determines the ``ActionType`` from the URI scheme and checks
347+
permissions before returning resource contents.
348+
349+
Args:
350+
params: JSON-RPC params containing ``"uri"``.
351+
352+
Returns:
353+
Resource contents dict with MCP ``contents`` format.
354+
355+
Raises:
356+
PermissionError: If governance denies the resource read.
357+
"""
263358
uri = params.get("uri", "")
264359

265360
self.logger.info(f"Resource read request: {uri}")
@@ -321,7 +416,21 @@ def _handle_prompts_get(self, params: Dict) -> Dict:
321416
}
322417

323418
def _map_tool_to_action(self, tool_name: str) -> Optional[ActionType]:
324-
"""Map an MCP tool name to an ActionType."""
419+
"""Map an MCP tool name to an ``ActionType``.
420+
421+
Resolution order:
422+
1. Exact match in ``self.tool_mapping`` (case-insensitive).
423+
2. Pattern-based heuristics (e.g. names containing ``"read"``
424+
and ``"file"`` map to ``FILE_READ``).
425+
3. Returns ``None`` for unrecognized tools (deny-by-default).
426+
427+
Args:
428+
tool_name: The MCP tool name to resolve.
429+
430+
Returns:
431+
The corresponding ``ActionType``, or ``None`` if the tool
432+
cannot be mapped (triggering a denial).
433+
"""
325434
tool_name_lower = tool_name.lower()
326435

327436
# Check exact match
@@ -411,11 +520,36 @@ def add_tool_mapping(self, tool_name: str, action_type: ActionType):
411520

412521

413522
class MCPServer:
414-
"""
415-
Simplified MCP Server with built-in governance.
416-
417-
This class provides a simple way to create an MCP-compliant server
418-
with Agent Control Plane governance built in.
523+
"""Simplified MCP Server with built-in governance.
524+
525+
Provides a high-level API for creating an MCP-compliant server with
526+
Agent Control Plane governance built in. Wraps an ``MCPAdapter``
527+
internally and exposes convenience methods for tool and resource
528+
registration.
529+
530+
Args:
531+
server_name: Human-readable name for this MCP server.
532+
control_plane: ``AgentControlPlane`` instance for governance.
533+
agent_context: ``AgentContext`` representing the server's agent.
534+
transport: Transport method — ``"stdio"`` (default) or ``"sse"``.
535+
logger: Optional logger instance.
536+
537+
Attributes:
538+
adapter: The underlying ``MCPAdapter`` that handles governance.
539+
server_name: Name of this server.
540+
transport: Active transport method.
541+
542+
Example:
543+
>>> from agent_control_plane import AgentControlPlane
544+
>>> from agent_control_plane.mcp_adapter import MCPServer
545+
>>>
546+
>>> cp = AgentControlPlane()
547+
>>> ctx = cp.create_agent("file-agent")
548+
>>> server = MCPServer("file-server", cp, ctx, transport="stdio")
549+
>>>
550+
>>> server.register_tool("read_file", handle_read, "Read a file")
551+
>>> server.register_resource("file://", handle_resource, "File resources")
552+
>>> server.start() # All calls are now governed
419553
"""
420554

421555
def __init__(

0 commit comments

Comments
 (0)