Data Model: MCP Tool Operations¶
Feature: 010-mcp-tool-operations Date: 2025-11-28
Entity Relationship Diagram¶
┌─────────────────────────────────────────────────────────────────┐
│ MCPToolConfig │
│ (Enhanced MCPTool model in models/tool.py) │
├─────────────────────────────────────────────────────────────────┤
│ name: str # Tool identifier │
│ description: str # Human-readable description │
│ type: Literal["mcp"] # Tool type discriminator │
│ server: str # MCP server package/path │
│ transport: TransportType # stdio|sse|websocket|http │
│ command: str | None # Stdio: command to run │
│ args: list[str] | None # Stdio: command arguments │
│ env: dict[str,str] | None # Environment variables │
│ envFile: str | None # Path to .env file │
│ encoding: str | None # Stdio: stream encoding │
│ url: str | None # HTTP/SSE/WS: server URL │
│ headers: dict[str,str] | None# HTTP/SSE: request headers │
│ timeout: float | None # Connection timeout │
│ sse_read_timeout: float|None # SSE read timeout │
│ terminate_on_close: bool|None# HTTP: terminate on close │
│ config: dict[str,Any] | None # Server-specific config │
│ load_tools: bool = True # Auto-discover tools │
│ load_prompts: bool = True # Auto-discover prompts │
│ request_timeout: int = 60 # Operation timeout (seconds) │
└─────────────────────────────────────────────────────────────────┘
│
│ creates
▼
┌─────────────────────────────────────────────────────────────────┐
│ MCPPluginWrapper │
│ (HoloDeck wrapper for SK MCP plugins) │
├─────────────────────────────────────────────────────────────────┤
│ name: str # Plugin name │
│ config: MCPToolConfig # Original configuration │
│ _plugin: MCPPluginBase # Underlying SK plugin │
│ _connected: bool # Connection state │
│ _discovered_tools: list # Discovered tool definitions │
│ _discovered_prompts: list # Discovered prompt definitions │
├─────────────────────────────────────────────────────────────────┤
│ + connect() -> None # Establish connection │
│ + disconnect() -> None # Close connection │
│ + call_tool(name, **kw) # Invoke MCP tool │
│ + get_prompt(name, **kw) # Get MCP prompt │
│ + list_tools() -> list # Get available tools │
│ + list_prompts() -> list # Get available prompts │
└─────────────────────────────────────────────────────────────────┘
│
│ wraps one of
▼
┌─────────────────────────────────────────────────────────────────┐
│ Semantic Kernel MCP Plugin Classes │
├─────────────────────────────────────────────────────────────────┤
│ MCPStdioPlugin # stdio transport │
│ MCPSsePlugin # SSE transport │
│ MCPWebsocketPlugin # WebSocket transport │
│ MCPStreamableHttpPlugin # Streamable HTTP transport │
└─────────────────────────────────────────────────────────────────┘
│
│ returns
▼
┌─────────────────────────────────────────────────────────────────┐
│ MCPToolResult │
│ (Converted MCP response for HoloDeck) │
├─────────────────────────────────────────────────────────────────┤
│ success: bool # Operation succeeded │
│ content: list[ContentBlock] # Response content blocks │
│ error: MCPError | None # Error details if failed │
│ metadata: dict[str, Any] # Additional response metadata │
└─────────────────────────────────────────────────────────────────┘
│
│ contains
▼
┌─────────────────────────────────────────────────────────────────┐
│ ContentBlock │
│ (Union type for MCP content) │
├─────────────────────────────────────────────────────────────────┤
│ TextContent: │
│ type: "text" │
│ text: str │
│ │
│ ImageContent: │
│ type: "image" │
│ data: str (base64) │
│ mime_type: str │
│ │
│ AudioContent: │
│ type: "audio" │
│ data: str (base64) │
│ mime_type: str │
│ │
│ BinaryContent: │
│ type: "binary" │
│ data: bytes │
│ mime_type: str | None │
│ uri: str | None │
└─────────────────────────────────────────────────────────────────┘
Entity Details¶
MCPToolConfig (Enhanced MCPTool)¶
Purpose: YAML configuration model for MCP tools in agent.yaml
Validation Rules:
- name: Required, non-empty string, unique within agent
- server: Required, non-empty string (npm package or local path)
- transport: Default "stdio", must be one of: stdio, sse, websocket, http
- command: Required for stdio transport, MUST be one of: npx, uvx, docker
- url: Required for sse, websocket, http transports
- env values: Must resolve via ${VAR} pattern at config load time
- request_timeout: Default 60, must be positive integer
Supported Commands (security constraint):
| Command | Runtime | Use Case |
|---------|---------|----------|
| npx | Node.js | npm-based MCP servers (e.g., @modelcontextprotocol/*) |
| uvx | Python/uv | Python MCP servers via uv package manager |
| docker | Docker | Containerized MCP servers |
Arbitrary commands are NOT allowed to prevent command injection attacks.
Transport-Specific Validation: | Transport | Required Fields | Optional Fields | |-----------|-----------------|-----------------| | stdio | command | args, env, envFile, encoding | | sse | url | headers, timeout, sse_read_timeout | | websocket | url | - | | http | url | headers, timeout, sse_read_timeout, terminate_on_close |
Environment Variable Resolution (DRY - reuse existing):
- ${VAR} pattern: Use holodeck.config.env_loader.substitute_env_vars()
- env_file loading: Use holodeck.config.env_loader.load_env_file()
TransportType¶
Purpose: Enum for supported MCP transport types
class TransportType(str, Enum):
STDIO = "stdio"
SSE = "sse"
WEBSOCKET = "websocket"
HTTP = "http"
CommandType¶
Purpose: Enum for allowed stdio commands (security constraint)
class CommandType(str, Enum):
NPX = "npx" # Node.js/npm package runner
UVX = "uvx" # Python/uv package runner
DOCKER = "docker" # Docker container runner
Validation: The command field in MCPToolConfig MUST be validated against this enum.
Invalid commands raise MCPConfigError at config load time.
MCPPluginWrapper¶
Purpose: HoloDeck adapter wrapping Semantic Kernel MCP plugins
State Transitions:
┌──────────────┐
│ CREATED │
│ (initial) │
└──────┬───────┘
│ connect()
▼
┌──────────────┐ ┌──────────────┐
│ ERROR │◄───│ CONNECTING │
│ (terminal) │ │ │
└──────────────┘ └──────┬───────┘
▲ │ success
│ ▼
│ ┌──────────────┐
│ │ CONNECTED │◄──┐
│ │ (active) │ │ tool notification
│ └──────┬───────┘───┘ (refresh tools)
│ │ disconnect()
│ ▼
│ ┌──────────────┐
└────────────│ DISCONNECTED│
error │ (terminal) │
└──────────────┘
MCPToolResult¶
Purpose: Standardized result from MCP tool invocation
Fields:
- success: Boolean indicating operation success
- content: List of ContentBlock items (may be empty)
- error: MCPError instance if failed, None if success
- metadata: Dict for tool-specific metadata (timing, request_id, etc.)
ContentBlock¶
Purpose: Union type representing MCP content types
Type Discrimination: Via type field (Literal discriminator)
Error Types¶
Note: Extends existing error hierarchy from holodeck.lib.errors (DRY compliance)
HoloDeckError (existing)
├── ConfigError (existing) ─────────────────────────────────────────┐
│ └── MCPConfigError # Invalid MCP configuration (NEW) │
│ ├── MissingTransportFieldError # Missing required field │
│ └── UnresolvedEnvVarError # Env var not found │
│ (Note: uses existing ConfigError from substitute_env_vars)
│
└── MCPError (NEW) # Base MCP runtime error
├── MCPConnectionError # Failed to connect to server
│ └── MCPTimeoutError # Connection/request timeout
├── MCPProtocolError # Protocol-level error from server
└── MCPToolNotFoundError # Tool not found on server
Reuse Note: UnresolvedEnvVarError is NOT needed - substitute_env_vars()
already raises ConfigError with the variable name. MCP code should let this
propagate without wrapping.
Configuration Examples¶
Stdio Transport (Default)¶
tools:
- name: filesystem
type: mcp
server: "@modelcontextprotocol/server-filesystem"
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem"]
config:
allowed_directories: ["/workspace/data"]
SSE Transport¶
tools:
- name: remote_api
type: mcp
server: "my-mcp-server"
transport: sse
url: "https://mcp.example.com/sse"
headers:
Authorization: "Bearer ${API_TOKEN}"
timeout: 30
sse_read_timeout: 120
WebSocket Transport¶
tools:
- name: realtime
type: mcp
server: "realtime-server"
transport: websocket
url: "wss://mcp.example.com/ws"
HTTP Transport (Streamable)¶
tools:
- name: streaming_api
type: mcp
server: "streaming-server"
transport: http
url: "https://mcp.example.com/stream"
terminate_on_close: true
Database Schema¶
N/A - MCP tools do not persist data to HoloDeck storage. MCP servers manage their own state.
Indexes and Constraints¶
N/A - No database tables.
Migration Notes¶
Existing MCPTool Model: The current MCPTool in models/tool.py has minimal fields:
- name, description, type, server, config
Migration Strategy:
1. Add new fields with defaults (backward compatible)
2. Existing configs with only server will use stdio transport with auto-detected command
3. No data migration needed (config-only change)