Skip to main content

MCIClient Class

The MCIClient class is the main entry point for the MCI Python adapter. It provides methods for loading, filtering, and executing MCI tools from a JSON or YAML schema file.

Initialization

MCIClient(schema_file_path=None, env_vars=None, json_file_path=None, validating=False)

Initialize the MCI client with a schema file and optional environment variables. Parameters:
NameTypeRequiredDescription
schema_file_pathstrConditional*Path to the MCI schema file (.json, .yaml, or .yml)
env_varsdict[str, Any]NoEnvironment variables for template substitution (default: {})
json_file_pathstrConditional*DEPRECATED. Use schema_file_path instead. Kept for backward compatibility.
validatingboolNoEnable pure schema validation mode without loading MCP servers, toolsets, or resolving templates. Tool execution is disabled in this mode. (default: False)
*Either schema_file_path or json_file_path must be provided. Raises:
  • MCIClientError - If the schema file cannot be loaded or parsed
  • MCIClientError - If neither schema_file_path nor json_file_path is provided
Example:
from mcipy import MCIClient

# Initialize with JSON file (recommended)
client = MCIClient(schema_file_path="example.mci.json")

# Initialize with YAML file
client = MCIClient(schema_file_path="example.mci.yaml")

# Initialize with environment variables
client = MCIClient(
    schema_file_path="example.mci.json",
    env_vars={
        "API_KEY": "your-secret-key",
        "USERNAME": "demo_user",
        "BEARER_TOKEN": "token-123"
    }
)

# Validating mode: validate schema without loading toolsets or MCP servers
# Useful for CI validation, IDE plugins, or schema checking
client = MCIClient(
    schema_file_path="example.mci.json",
    validating=True  # No env vars needed, no side effects
)

# Check what tools are defined (inline tools only in validating mode)
tool_names = client.list_tools()

# Backward compatibility: json_file_path still works
client = MCIClient(json_file_path="example.mci.json")
# Works with YAML too
client = MCIClient(json_file_path="example.mci.yaml")
Success Response: Returns an initialized MCIClient instance ready to use. Error Response:
# Raises MCIClientError
MCIClientError: Failed to load schema from invalid.json: [Errno 2] No such file or directory: 'invalid.json'

# Unsupported file extension
MCIClientError: Failed to load schema from file.txt: Unsupported file extension '.txt'. Supported extensions: .json, .yaml, .yml

Validating Mode

When validating=True is specified, the client operates in a special validation-only mode: What happens in validating mode:
  • ✅ Schema file is parsed and validated (JSON/YAML syntax, structure, types)
  • ✅ Schema version is checked for compatibility
  • ✅ Tool definitions are validated (required fields, execution types)
  • ✅ Toolset files are checked for existence (but not loaded)
  • ✅ MCP server configurations are validated (but servers are not contacted)
  • ✅ Read-only operations work normally (list_tools(), tools(), only(), without(), etc.)
What does NOT happen in validating mode:
  • ❌ No template resolution (placeholders like {{env.VAR}} are accepted as-is)
  • ❌ No MCP server connections or tool fetching
  • ❌ No toolset file loading (only existence is checked)
  • ❌ No file writes or cache directory creation
  • ❌ No network requests
  • ❌ Tool execution is blocked (raises MCIClientError)
Use cases for validating mode:
  • CI/CD validation: Check schema validity without requiring environment variables
  • IDE/Editor plugins: Validate schemas and provide autocomplete without side effects
  • Schema testing: Verify schema structure before deployment
  • Documentation generation: Parse schemas to generate tool documentation
  • Pre-deployment checks: Validate schemas before committing to version control
Example:
from mcipy import MCIClient, MCIClientError

# Validate a schema with MCP servers that require env vars
client = MCIClient(
    schema_file_path="schema_with_mcp.mci.json",
    validating=True  # No env vars needed!
)

# This works - checking what tools are defined
print(f"Schema contains {len(client.list_tools())} inline tools")

# This works - listing toolsets (just checks they exist)
# Toolsets are not loaded, so tools from toolsets won't appear in list_tools()

# This raises an error - execution is disabled
try:
    client.execute("some_tool", {})
except MCIClientError as e:
    print(f"Expected error: {e}")
    # Output: Tool execution is disabled in validating mode. Initialize MCIClient with validating=False to execute tools.

Methods

tools()

Get all available tools from the loaded schema. Method Signature:
def tools(self) -> list[Tool]
Parameters: None Returns:
TypeDescription
list[Tool]List of all Tool objects in the schema
Example:
from mcipy import MCIClient

client = MCIClient(json_file_path="example.mci.json")
all_tools = client.tools()

for tool in all_tools:
    print(f"Tool: {tool.name} - {tool.description}")
Success Response:
[
    Tool(
        name="get_weather",
        annotations=Annotations(title="Get Weather Information"),
        description="Fetch current weather information for a location",
        inputSchema={
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "City name or location"}
            },
            "required": ["location"]
        },
        execution=HTTPExecutionConfig(...)
    ),
    Tool(
        name="create_report",
        annotations=Annotations(title="Create Report"),
        description="Create a new report using HTTP POST request",
        inputSchema={...},
        execution=HTTPExecutionConfig(...)
    )
]
Error Response: No errors - always returns a list (may be empty if no tools defined).

only()

Filter tools to include only specified tools by name. Method Signature:
def only(self, tool_names: list[str]) -> list[Tool]
Parameters:
NameTypeRequiredDescription
tool_nameslist[str]YesList of tool names to include
Returns:
TypeDescription
list[Tool]Filtered list of Tool objects matching the specified names
Example:
from mcipy import MCIClient

client = MCIClient(json_file_path="example.mci.json")

# Get only weather-related tools
weather_tools = client.only(["get_weather", "get_forecast"])

for tool in weather_tools:
    print(f"Weather tool: {tool.name}")
Success Response:
[
    Tool(
        name="get_weather",
        annotations=Annotations(title="Get Weather Information"),
        description="Fetch current weather information for a location",
        inputSchema={...},
        execution=HTTPExecutionConfig(...)
    ),
    Tool(
        name="get_forecast",
        annotations=Annotations(title="Get Weather Forecast"),
        description="Get weather forecast for the next 7 days",
        inputSchema={...},
        execution=HTTPExecutionConfig(...)
    )
]
Error Response: No errors - returns empty list if no tools match the specified names.

without()

Filter tools to exclude specified tools by name. Method Signature:
def without(self, tool_names: list[str]) -> list[Tool]
Parameters:
NameTypeRequiredDescription
tool_nameslist[str]YesList of tool names to exclude
Returns:
TypeDescription
list[Tool]Filtered list of Tool objects excluding the specified names
Example:
from mcipy import MCIClient

client = MCIClient(json_file_path="example.mci.json")

# Get all tools except dangerous ones
safe_tools = client.without(["delete_data", "admin_tools"])

for tool in safe_tools:
    print(f"Safe tool: {tool.name}")
Success Response:
[
    Tool(
        name="get_weather",
        annotations=Annotations(title="Get Weather Information"),
        description="Fetch current weather information for a location",
        inputSchema={...},
        execution=HTTPExecutionConfig(...)
    ),
    Tool(
        name="search_data",
        annotations=Annotations(title="Search Data"),
        description="Search for data in the database",
        inputSchema={...},
        execution=HTTPExecutionConfig(...)
    )
    # delete_data and admin_tools are excluded
]
Error Response: No errors - returns all tools if specified names don’t exist.

tags()

Filter tools to include only those with at least one matching tag. Method Signature:
def tags(self, tags: list[str]) -> list[Tool]
Parameters:
NameTypeRequiredDescription
tagslist[str]YesList of tags to filter by (OR logic - tool must have at least one matching tag)
Returns:
TypeDescription
list[Tool]Filtered list of Tool objects that have at least one of the specified tags
Example:
from mcipy import MCIClient

client = MCIClient(schema_file_path="example.mci.json")

# Get all tools tagged with "api" or "database"
api_or_db_tools = client.tags(["api", "database"])

for tool in api_or_db_tools:
    print(f"Tool: {tool.name}, Tags: {tool.tags}")
Success Response:
[
    Tool(
        name="github_api",
        description="GitHub API client",
        tags=["api", "external"],
        execution=HTTPExecutionConfig(...)
    ),
    Tool(
        name="database_query",
        description="Query database",
        tags=["database", "internal"],
        execution=CLIExecutionConfig(...)
    )
]
Error Response: No errors - returns empty list if no tools have any of the specified tags. Notes:
  • Tags are case-sensitive and matched exactly as provided
  • Uses OR logic: a tool is included if it has ANY of the specified tags
  • Tools without tags are never included
  • Empty tag list returns empty result

withoutTags()

Filter tools to exclude those with any matching tag. Method Signature:
def withoutTags(self, tags: list[str]) -> list[Tool]
Parameters:
NameTypeRequiredDescription
tagslist[str]YesList of tags to exclude (OR logic - tool is excluded if it has any matching tag)
Returns:
TypeDescription
list[Tool]Filtered list of Tool objects that do NOT have any of the specified tags
Example:
from mcipy import MCIClient

client = MCIClient(schema_file_path="example.mci.json")

# Get all tools that are NOT tagged with "external" or "deprecated"
internal_tools = client.withoutTags(["external", "deprecated"])

for tool in internal_tools:
    print(f"Internal tool: {tool.name}")
Success Response:
[
    Tool(
        name="database_query",
        description="Query database",
        tags=["database", "internal"],
        execution=CLIExecutionConfig(...)
    ),
    Tool(
        name="generate_report",
        description="Generate internal report",
        tags=["internal", "reporting"],
        execution=TextExecutionConfig(...)
    )
    # Tools with "external" or "deprecated" tags are excluded
]
Error Response: No errors - returns all tools if none have any of the specified tags. Notes:
  • Tags are case-sensitive and matched exactly as provided
  • Uses OR logic for exclusion: a tool is excluded if it has ANY of the specified tags
  • Tools without tags are always included (they don’t have the excluded tags)
  • Empty tag list returns all tools

toolsets()

Filter tools to include only those from specified toolsets. Method Signature:
def toolsets(self, toolset_names: list[str]) -> list[Tool]
Parameters:
NameTypeRequiredDescription
toolset_nameslist[str]YesList of toolset names to include (OR logic - tool is included if it came from any matching toolset)
Returns:
TypeDescription
list[Tool]Filtered list of Tool objects from the specified toolsets
Example:
from mcipy import MCIClient

client = MCIClient(schema_file_path="example.mci.json")

# Get all tools from the "weather" toolset
weather_tools = client.toolsets(["weather"])

# Get tools from multiple toolsets
api_tools = client.toolsets(["weather", "database", "github"])

for tool in api_tools:
    print(f"Tool: {tool.name} (from {tool.toolset_source})")
Success Response:
[
    Tool(
        name="get_weather",
        description="Get current weather",
        tags=["weather", "read"],
        toolset_source="weather",
        execution=HTTPExecutionConfig(...)
    ),
    Tool(
        name="query_data",
        description="Query database",
        tags=["database", "read"],
        toolset_source="database",
        execution=CLIExecutionConfig(...)
    )
]
Error Response: No errors - returns empty list if no tools match the specified toolset names. Notes:
  • Only returns tools that were loaded from toolsets (not main schema tools)
  • Uses OR logic: a tool is included if it came from ANY of the specified toolsets
  • Toolset filtering respects schema-level filters (only tools registered by their toolset’s filter are included)
  • Empty toolset list returns no tools
  • Tools must have been loaded via the toolsets field in the main schema
  • The toolset_source field on each Tool indicates which toolset it came from

execute()

Execute a tool by name with the provided properties. Method Signature:
def execute(self, tool_name: str, properties: dict[str, Any] | None = None) -> ExecutionResult
Parameters:
NameTypeRequiredDescription
tool_namestrYesName of the tool to execute
propertiesdict[str, Any]NoProperties/parameters to pass to the tool (default: {})
Returns:
TypeDescription
ExecutionResultResult object with success/error status and content
Raises:
  • MCIClientError - If tool not found or execution fails with validation error
Example:
from mcipy import MCIClient

client = MCIClient(
    json_file_path="example.mci.json",
    env_vars={"API_KEY": "your-secret-key"}
)

# Execute a tool with properties
result = client.execute(
    tool_name="get_weather",
    properties={"location": "New York"}
)

# Handle result
if result.result.isError:
    print(f"Error: {result.result.content[0].text}")
else:
    print(f"Success: {result.result.content[0].text}")
    if result.metadata:
        print(f"Metadata: {result.metadata}")
Success Response:
ExecutionResult(
    isError=False,
    content={
        "location": "New York",
        "temperature": 72,
        "conditions": "Sunny",
        "humidity": 45
    },
    error=None,
    metadata={
        "status_code": 200,
        "execution_time_ms": 150
    }
)
Error Response - Tool Not Found:
# Raises MCIClientError
MCIClientError: Tool not found: invalid_tool_name
Error Response - Execution Error:
ExecutionResult(
    isError=True,
    content=None,
    error="HTTP request failed: 404 Not Found",
    metadata={
        "status_code": 404,
        "execution_time_ms": 75
    }
)
Error Response - Network Error:
ExecutionResult(
    isError=True,
    content=None,
    error="Connection timeout after 5000ms",
    metadata=None
)

list_tools()

List available tool names as strings. Method Signature:
def list_tools(self) -> list[str]
Parameters: None Returns:
TypeDescription
list[str]List of tool names (strings)
Example:
from mcipy import MCIClient

client = MCIClient(json_file_path="example.mci.json")
tool_names = client.list_tools()

print(f"Available tools: {tool_names}")
Success Response:
["get_weather", "create_report", "search_files", "load_file", "generate_text"]
Error Response: No errors - returns empty list if no tools defined.

get_tool_schema()

Get a tool’s input schema (JSON Schema format). Method Signature:
def get_tool_schema(self, tool_name: str) -> dict[str, Any]
Parameters:
NameTypeRequiredDescription
tool_namestrYesName of the tool
Returns:
TypeDescription
dict[str, Any]Tool’s input schema as a dictionary (or empty dict if no schema)
Raises:
  • MCIClientError - If tool not found
Example:
from mcipy import MCIClient

client = MCIClient(json_file_path="example.mci.json")
schema = client.get_tool_schema("get_weather")

print(f"Schema: {schema}")
Success Response:
{
    "type": "object",
    "properties": {
        "location": {
            "type": "string",
            "description": "City name or location"
        },
        "unit": {
            "type": "string",
            "description": "Temperature unit (celsius or fahrenheit)",
            "enum": ["celsius", "fahrenheit"]
        }
    },
    "required": ["location"]
}
Success Response - No Schema:
{}  # Empty dict if tool has no input schema
Error Response:
# Raises MCIClientError
MCIClientError: Tool not found: invalid_tool_name

Data Models

MCISchema

Top-level MCI schema representing the complete MCI context file. Fields:
FieldTypeRequiredDescription
schemaVersionstrYesSchema version (e.g., “1.0”)
metadataMetadataNoOptional metadata about the tool collection
toolslist[Tool]YesList of tool definitions
Example:
{
    "schemaVersion": "1.0",
    "metadata": {
        "name": "Example MCI Tools",
        "description": "Example tool collection",
        "version": "1.0.0",
        "license": "MIT",
        "authors": ["MCI Team"]
    },
    "tools": [
        {
            "name": "get_weather",
            "annotations": {
        "title": "Get Weather"
            },
            "description": "Get weather information",
            "inputSchema": {...},
            "execution": {...}
        }
    ]
}

Tool

Individual tool definition with name, description, input schema, and execution configuration. Fields:
FieldTypeRequiredDescription
namestrYesUnique identifier for the tool
annotationsAnnotationsNoTool metadata and hints
disabledboolNoIf true, tool is ignored (default: false)
descriptionstrNoDescription of what the tool does
inputSchemadict[str, Any]NoJSON Schema defining expected input properties
executionHTTPExecutionConfig | CLIExecutionConfig | FileExecutionConfig | TextExecutionConfigYesExecution configuration (determines how tool is executed)
Example:
{
    "name": "get_weather",
    "annotations": {
        "title": "Get Weather Information"
    },
    "description": "Fetch current weather information for a location",
    "inputSchema": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "City name or location"
            }
        },
        "required": ["location"]
    },
    "execution": {
        "type": "http",
        "method": "GET",
        "url": "https://api.example.com/weather",
        "params": {
            "location": "{{props.location}}"
        }
    }
}

ExecutionResult

Result format returned from tool execution. Fields:
FieldTypeRequiredDescription
isErrorboolYesWhether an error occurred during execution
contentAnyNoResult content (None if error)
errorstrNoError message (None if success)
metadatadict[str, Any]NoAdditional metadata (e.g., status_code, execution_time_ms)
Example - Success:
ExecutionResult(
    isError=False,
    content={
        "location": "New York",
        "temperature": 72,
        "conditions": "Sunny"
    },
    error=None,
    metadata={
        "status_code": 200,
        "execution_time_ms": 150
    }
)
Example - Error:
ExecutionResult(
    isError=True,
    content=None,
    error="HTTP request failed: 404 Not Found",
    metadata={
        "status_code": 404,
        "execution_time_ms": 75
    }
)
Example - Text Content:
ExecutionResult(
    isError=False,
    content="Hello, World!",
    error=None,
    metadata=None
)
Example - File Content:
ExecutionResult(
    isError=False,
    content="File content with template: user@example.com",
    error=None,
    metadata=None
)

Metadata

Optional metadata about the MCI tool collection. Fields:
FieldTypeRequiredDescription
namestrNoName of the tool collection
descriptionstrNoDescription of the collection
versionstrNoVersion number (e.g., “1.0.0”)
licensestrNoLicense type (e.g., “MIT”)
authorslist[str]NoList of author names
Example:
{
    "name": "Weather Tools",
    "description": "Collection of weather-related tools",
    "version": "1.0.0",
    "license": "MIT",
    "authors": ["Alice Smith", "Bob Jones"]
}

Execution Configurations

HTTPExecutionConfig

Configuration for HTTP-based tool execution. Fields:
FieldTypeRequiredDefaultDescription
typeExecutionTypeYes"http"Execution type identifier
methodstrNo"GET"HTTP method (GET, POST, PUT, DELETE, etc.)
urlstrYes-URL endpoint for the request
headersdict[str, str]NoNoneHTTP headers
authAuthConfigNoNoneAuthentication configuration
paramsdict[str, Any]NoNoneQuery parameters
bodyHTTPBodyConfigNoNoneRequest body configuration
timeout_msintNo30000Request timeout in milliseconds
retriesRetryConfigNoNoneRetry configuration
Example - GET Request:
{
    "type": "http",
    "method": "GET",
    "url": "https://api.example.com/weather",
    "params": {
        "location": "{{props.location}}",
        "units": "metric"
    },
    "headers": {
        "Accept": "application/json"
    },
    "timeout_ms": 5000
}
Example - POST Request with Authentication:
{
    "type": "http",
    "method": "POST",
    "url": "https://api.example.com/reports",
    "headers": {
        "Content-Type": "application/json"
    },
    "auth": {
        "type": "bearer",
        "token": "{{env.BEARER_TOKEN}}"
    },
    "body": {
        "type": "json",
        "content": {
            "title": "{{props.title}}",
            "content": "{{props.content}}"
        }
    },
    "timeout_ms": 10000,
    "retries": {
        "attempts": 3,
        "backoff_ms": 1000
    }
}

CLIExecutionConfig

Configuration for command-line tool execution. Fields:
FieldTypeRequiredDefaultDescription
typeExecutionTypeYes"cli"Execution type identifier
commandstrYes-Command to execute
argslist[str]NoNoneCommand arguments
flagsdict[str, FlagConfig]NoNoneCommand flags configuration
cwdstrNoNoneWorking directory for command execution
timeout_msintNo30000Execution timeout in milliseconds
Example - Simple Command:
{
    "type": "cli",
    "command": "ls",
    "args": ["-la", "/home/user"],
    "timeout_ms": 5000
}
Example - Command with Flags:
{
    "type": "cli",
    "command": "grep",
    "args": ["-r", "{{props.pattern}}"],
    "flags": {
        "--color": {
            "from": "props.color",
            "type": "boolean"
        }
    },
    "cwd": "/home/user/projects",
    "timeout_ms": 10000
}

FileExecutionConfig

Configuration for file reading and templating. Fields:
FieldTypeRequiredDefaultDescription
typeExecutionTypeYes"file"Execution type identifier
pathstrYes-Path to the file to read
enableTemplatingboolNoTrueWhether to process template placeholders in file content
Example - Read File with Templating:
{
    "type": "file",
    "path": "/home/user/templates/email.txt",
    "enableTemplating": true
}
Example - Read File Without Templating:
{
    "type": "file",
    "path": "/home/user/data/config.json",
    "enableTemplating": false
}

TextExecutionConfig

Configuration for simple text template execution. Fields:
FieldTypeRequiredDefaultDescription
typeExecutionTypeYes"text"Execution type identifier
textstrYes-Text template with placeholder support
Example:
{
    "type": "text",
    "text": "Hello {{props.name}}! Your email is {{env.USER_EMAIL}}."
}
Execution Result:
# With properties={"name": "Alice"} and env_vars={"USER_EMAIL": "alice@example.com"}
ExecutionResult(
    isError=False,
    content="Hello Alice! Your email is alice@example.com.",
    error=None,
    metadata=None
)

Authentication Models

ApiKeyAuth

API Key authentication configuration. Fields:
FieldTypeRequiredDefaultDescription
typestrYes"apiKey"Authentication type
instrYes-Where to place the key: “header” or “query”
namestrYes-Name of the header or query parameter
valuestrYes-API key value (supports templates)
Example - Header-based:
{
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key",
    "value": "{{env.API_KEY}}"
}
Example - Query parameter:
{
    "type": "apiKey",
    "in": "query",
    "name": "api_key",
    "value": "{{env.API_KEY}}"
}

BearerAuth

Bearer token authentication configuration. Fields:
FieldTypeRequiredDefaultDescription
typestrYes"bearer"Authentication type
tokenstrYes-Bearer token value (supports templates)
Example:
{
    "type": "bearer",
    "token": "{{env.BEARER_TOKEN}}"
}

BasicAuth

Basic authentication (username/password) configuration. Fields:
FieldTypeRequiredDefaultDescription
typestrYes"basic"Authentication type
usernamestrYes-Username (supports templates)
passwordstrYes-Password (supports templates)
Example:
{
    "type": "basic",
    "username": "{{env.USERNAME}}",
    "password": "{{env.PASSWORD}}"
}

OAuth2Auth

OAuth2 authentication configuration. Fields:
FieldTypeRequiredDefaultDescription
typestrYes"oauth2"Authentication type
flowstrYes-OAuth2 flow type (e.g., “clientCredentials”)
tokenUrlstrYes-Token endpoint URL
clientIdstrYes-OAuth2 client ID
clientSecretstrYes-OAuth2 client secret (supports templates)
scopeslist[str]NoNoneOptional OAuth2 scopes
Example:
{
    "type": "oauth2",
    "flow": "clientCredentials",
    "tokenUrl": "https://auth.example.com/oauth/token",
    "clientId": "my-client-id",
    "clientSecret": "{{env.OAUTH_CLIENT_SECRET}}",
    "scopes": ["read:data", "write:data"]
}

Error Handling

The MCI Python adapter provides consistent error handling across all operations.

Exception Types

MCIClientError

Raised by MCIClient methods for client-level errors. Common Causes:
  • Schema file not found or invalid
  • Tool not found
  • Invalid tool execution
Example:
from mcipy import MCIClient, MCIClientError

try:
    client = MCIClient(json_file_path="nonexistent.json")
except MCIClientError as e:
    print(f"Client error: {e}")
    # Output: Client error: Failed to load schema from nonexistent.json: [Errno 2] No such file or directory

ExecutionResult Error Format

Execution errors are returned as ExecutionResult objects with isError=True. Error Fields:
FieldDescription
isErrorAlways True for errors
contentAlways None for errors
errorHuman-readable error message
metadataOptional additional error context
Example Error Scenarios: HTTP Request Failed:
result = client.execute("get_weather", {"location": "InvalidCity"})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="HTTP request failed: 404 Not Found",
#     metadata={"status_code": 404, "execution_time_ms": 75}
# )
Connection Timeout:
result = client.execute("slow_api", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="Connection timeout after 30000ms",
#     metadata=None
# )
CLI Command Failed:
result = client.execute("invalid_command", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="Command failed with exit code 127: command not found",
#     metadata={"exit_code": 127}
# )
File Not Found:
result = client.execute("read_config", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="File not found: /path/to/config.json",
#     metadata=None
# )
Template Variable Missing:
# If {{env.MISSING_VAR}} is referenced but not provided
result = client.execute("template_tool", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="Template variable not found: env.MISSING_VAR",
#     metadata=None
# )
Path Validation Error:
# When a tool attempts to access a file outside allowed directories
result = client.execute("read_file", {"path": "/etc/passwd"})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="File path access outside context directory and allow-list is not allowed unless enableAnyPaths is true. Path: /etc/passwd",
#     metadata=None
# )

Security: Path Validation

The MCI Python adapter includes built-in path validation to prevent unauthorized file system access. Default Behavior:
  • File and CLI execution are restricted to the schema file’s directory
  • Subdirectories of the schema directory are allowed
  • Paths outside the schema directory are blocked unless explicitly allowed
Configuration Options:
  1. Schema-level settings (applies to all tools):
    {
      "schemaVersion": "1.0",
      "enableAnyPaths": false,
      "directoryAllowList": ["/home/user/data", "./configs"],
      "tools": [...]
    }
    
  2. Tool-level settings (overrides schema-level):
    {
      "name": "read_system_file",
      "enableAnyPaths": true,
      "execution": {
        "type": "file",
        "path": "{{props.file_path}}"
      }
    }
    
Path Validation Behavior:
ScenarioAllowed?
File in schema directory✓ Yes
File in subdirectory of schema directory✓ Yes
File outside schema directory (no config)✗ No - Error
File in directoryAllowList✓ Yes
Any path with enableAnyPaths: true✓ Yes
Best Practices:
  • Keep enableAnyPaths disabled unless absolutely necessary
  • Use directoryAllowList for specific directories instead of enableAnyPaths
  • Validate user input before passing to tools that access files
  • Review tool configurations regularly for security implications

Error Handling Best Practices

Check isError Flag:
result = client.execute("get_weather", {"location": "New York"})

if result.result.isError:
    print(f"Error occurred: {result.result.content[0].text}")
    if result.metadata:
        print(f"Additional context: {result.metadata}")
else:
    print(f"Success: {result.result.content[0].text}")
Try-Except for Client Errors:
try:
    client = MCIClient(json_file_path="example.mci.json")
    result = client.execute("get_weather", {"location": "New York"})

    if result.result.isError:
        # Handle execution errors
        print(f"Execution failed: {result.result.content[0].text}")
    else:
        # Process successful result
        print(f"Weather data: {result.result.content[0].text}")

except MCIClientError as e:
    # Handle client-level errors (tool not found, invalid schema, etc.)
    print(f"Client error: {e}")
Validate Tools Before Execution:
client = MCIClient(json_file_path="example.mci.json")

# Check if tool exists
available_tools = client.list_tools()
if "get_weather" in available_tools:
    result = client.execute("get_weather", {"location": "New York"})
else:
    print("Tool 'get_weather' not available")

Complete Usage Example

Here’s a comprehensive example demonstrating all major features:
from mcipy import MCIClient, MCIClientError

# Initialize client with environment variables
try:
    client = MCIClient(
        json_file_path="example.mci.json",
        env_vars={
            "API_KEY": "your-secret-key",
            "BEARER_TOKEN": "bearer-token-123",
            "USERNAME": "demo_user"
        }
    )
except MCIClientError as e:
    print(f"Failed to initialize client: {e}")
    exit(1)

# List all available tools
print("Available tools:")
for tool_name in client.list_tools():
    print(f"  - {tool_name}")

# Get detailed tool information
all_tools = client.tools()
for tool in all_tools:
    print(f"\nTool: {tool.name}")
    print(f"  Title: {tool.annotations.title if tool.annotations else \'N/A\'}")
    print(f"  Description: {tool.description}")

# Filter tools
weather_tools = client.only(["get_weather", "get_forecast"])
print(f"\nWeather tools: {[t.name for t in weather_tools]}")

safe_tools = client.without(["delete_data", "admin_tools"])
print(f"Safe tools: {[t.name for t in safe_tools]}")

# Get tool schema
try:
    schema = client.get_tool_schema("get_weather")
    print(f"\nWeather tool schema: {schema}")
except MCIClientError as e:
    print(f"Error getting schema: {e}")

# Execute a tool
result = client.execute(
    tool_name="get_weather",
    properties={"location": "New York"}
)

if result.result.isError:
    print(f"\nExecution failed: {result.error}")
    if result.metadata:
        print(f"Error metadata: {result.metadata}")
else:
    print(f"\nExecution successful!")
    print(f"Content: {result.result.content[0].text}")
    if result.metadata:
        print(f"Metadata: {result.metadata}")

Template Syntax

MCI supports template placeholders for dynamic value substitution:
  • {{props.fieldName}} - Access properties passed to execute()
  • {{env.VARIABLE_NAME}} - Access environment variables
  • {{input.fieldName}} - Deprecated alias for props (use props instead)
Note: {{input.fieldName}} is supported for backward compatibility but is deprecated. Use {{props.fieldName}} in all new code. Example:
{
  "execution": {
    "type": "http",
    "url": "https://api.example.com/users/{{props.userId}}",
    "headers": {
      "Authorization": "Bearer {{env.API_TOKEN}}"
    }
  }
}
With execution:
result = client.execute(
    "get_user",
    properties={"userId": "12345"}
)
# Resolves to: https://api.example.com/users/12345

Notes

  • All methods are synchronous (blocking) - execution waits for completion
  • Environment variables should be provided during initialization, not at execution time
  • Templates are processed before execution using a simple {{}} placeholder substitution system (not full Jinja2 syntax)
  • HTTP responses are automatically parsed as JSON when possible
  • CLI commands capture both stdout and stderr
  • File paths can be relative or absolute
  • Timeout values are in milliseconds
  • All string fields support template substitution unless explicitly disabled

LiteMcpClient Class

The LiteMcpClient class provides a lightweight integration with MCP (Model Context Protocol) servers using the official mcp package. It allows connecting to MCP tool servers via STDIO (e.g., uvx, npx) and HTTP/SSE endpoints.

Configuration Models

StdioCfg

Configuration for STDIO-based MCP servers (local servers via command-line). Fields:
NameTypeRequiredDescription
typeLiteral["stdio"]YesMust be “stdio”
commandstrYesCommand to run (e.g., “uvx”, “npx”)
argsList[str]NoArguments to pass to the command (default: [])
envDict[str, str]NoEnvironment variables for the server process (default: {})
Example:
from mcipy import StdioCfg

# STDIO configuration for uvx
stdio_cfg = StdioCfg(
    command="uvx",
    args=["mcp-server-memory"],
    env={"API_KEY": "secret"}
)

# STDIO configuration for npx
stdio_cfg = StdioCfg(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-memory"]
)

SseCfg

Configuration for HTTP/SSE-based MCP servers (web-based servers). Fields:
NameTypeRequiredDescription
typeLiteral["http"]YesMust be “http”
urlHttpUrlYesServer URL (e.g., “http://localhost:8000/mcp”)
headersDict[str, str]NoHTTP headers for authentication (default: {})
Example:
from mcipy import SseCfg

# HTTP configuration with authentication
http_cfg = SseCfg(
    url="https://api.example.com/mcp",
    headers={"Authorization": "Bearer token123"}
)

# HTTP configuration without authentication
http_cfg = SseCfg(url="http://localhost:8000/mcp")

ClientCfg

Complete configuration for the LiteMcpClient. Fields:
NameTypeRequiredDescription
serverStdioCfg | SseCfgYesServer connection configuration
request_timeoutOptional[float]NoRequest timeout in seconds (default: 60.0)
Example:
from mcipy import ClientCfg, StdioCfg

# Client configuration with STDIO server
client_cfg = ClientCfg(
    server=StdioCfg(command="uvx", args=["mcp-server"]),
    request_timeout=120.0
)

Initialization

LiteMcpClient(cfg: ClientCfg)

Initialize the LiteMcpClient with configuration. Parameters:
NameTypeRequiredDescription
cfgClientCfgYesClient configuration specifying server type and connection details
Example:
from mcipy import LiteMcpClient, ClientCfg, StdioCfg

cfg = ClientCfg(
    server=StdioCfg(command="uvx", args=["mcp-server-memory"])
)
client = LiteMcpClient(cfg)

Usage

The LiteMcpClient must be used as an async context manager to properly manage the connection lifecycle. Example:
import asyncio
from mcipy import LiteMcpClient, ClientCfg, StdioCfg

async def main():
    cfg = ClientCfg(
        server=StdioCfg(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-memory"]
        )
    )

    async with LiteMcpClient(cfg) as client:
        # List available tools
        tools = await client.list_tools()
        print(f"Available tools: {tools}")

        # Call a tool
        result = await client.call_tool("store_memory", key="test", value="data")
        print(f"Result: {result}")

asyncio.run(main())

Methods

async list_tools() -> List[str]

List all available tools from the MCP server. Returns:
  • List[str] - List of tool names available on the server
Raises:
  • RuntimeError - If session is not initialized (client not used as context manager)
Example:
async with LiteMcpClient(cfg) as client:
    tools = await client.list_tools()
    # Returns: ["store_memory", "retrieve_memory", "list_memories"]

async call_tool(name: str, **arguments: Any) -> Any

Call a tool on the MCP server with the provided arguments. Parameters:
NameTypeRequiredDescription
namestrYesName of the tool to call
**argumentsAnyNoKeyword arguments to pass to the tool
Returns:
  • Any - The tool execution result from the server (typically containing content and metadata)
Raises:
  • RuntimeError - If session is not initialized (client not used as context manager)
Example:
async with LiteMcpClient(cfg) as client:
    # Call tool with arguments
    result = await client.call_tool(
        "store_memory",
        key="user_preference",
        value="dark_mode"
    )
    print(result.content)  # Access result content

Complete Examples

STDIO Example (uvx)

import asyncio
from mcipy import LiteMcpClient, ClientCfg, StdioCfg

async def main():
    cfg = ClientCfg(
        server=StdioCfg(
            command="uvx",
            args=["mcp-server-memory"],
            env={}
        )
    )

    async with LiteMcpClient(cfg) as client:
        tools = await client.list_tools()
        print(f"Available tools: {tools}")

        if "store_memory" in tools:
            await client.call_tool(
                "store_memory",
                key="greeting",
                value="Hello, World!"
            )

asyncio.run(main())

STDIO Example (npx)

import asyncio
from mcipy import LiteMcpClient, ClientCfg, StdioCfg

async def main():
    cfg = ClientCfg(
        server=StdioCfg(
            command="npx",
            args=["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
        )
    )

    async with LiteMcpClient(cfg) as client:
        tools = await client.list_tools()
        print(f"Filesystem tools: {tools}")

asyncio.run(main())

HTTP Example

import asyncio
from mcipy import LiteMcpClient, ClientCfg, SseCfg

async def main():
    cfg = ClientCfg(
        server=SseCfg(
            url="https://api.githubcopilot.com/mcp/",
            headers={"Authorization": "Bearer YOUR_TOKEN"}
        )
    )

    async with LiteMcpClient(cfg) as client:
        tools = await client.list_tools()
        print(f"GitHub MCP tools: {tools}")

asyncio.run(main())

Error Handling

RuntimeError: Raised when attempting to use methods outside of context manager:
cfg = ClientCfg(server=StdioCfg(command="uvx"))
client = LiteMcpClient(cfg)

# This will raise RuntimeError
await client.list_tools()  # Error: Session not initialized

# Correct usage:
async with client:
    await client.list_tools()  # Works correctly
Connection Errors: Network or process errors are propagated from the underlying MCP client:
try:
    async with LiteMcpClient(cfg) as client:
        tools = await client.list_tools()
except Exception as e:
    print(f"Connection failed: {e}")

Notes

  • The LiteMcpClient uses the official mcp package for MCP protocol communication
  • STDIO transport merges environment variables from the configuration with the current process environment
  • HTTP transport uses Streamable HTTP, the modern replacement for SSE
  • All async operations must be called from within the context manager
  • The client automatically handles connection setup and teardown