> ## Documentation Index
> Fetch the complete documentation index at: https://usemci.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> This document provides a comprehensive API reference for the Python MCI Adapter (`mcipy`)

## 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:**

| Name               | Type             | Required      | Description                                                                                                                                                   |
| ------------------ | ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema_file_path` | `str`            | Conditional\* | Path to the MCI schema file (`.json`, `.yaml`, or `.yml`)                                                                                                     |
| `env_vars`         | `dict[str, Any]` | No            | Environment variables for template substitution (default: `{}`)                                                                                               |
| `json_file_path`   | `str`            | Conditional\* | **DEPRECATED.** Use `schema_file_path` instead. Kept for backward compatibility.                                                                              |
| `validating`       | `bool`           | No            | Enable 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:**

```python theme={null}
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:**

```python theme={null}
# 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:**

```python theme={null}
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:**

```python theme={null}
def tools(self) -> list[Tool]
```

**Parameters:**

None

**Returns:**

| Type         | Description                            |
| ------------ | -------------------------------------- |
| `list[Tool]` | List of all Tool objects in the schema |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def only(self, tool_names: list[str]) -> list[Tool]
```

**Parameters:**

| Name         | Type        | Required | Description                   |
| ------------ | ----------- | -------- | ----------------------------- |
| `tool_names` | `list[str]` | Yes      | List of tool names to include |

**Returns:**

| Type         | Description                                                |
| ------------ | ---------------------------------------------------------- |
| `list[Tool]` | Filtered list of Tool objects matching the specified names |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def without(self, tool_names: list[str]) -> list[Tool]
```

**Parameters:**

| Name         | Type        | Required | Description                   |
| ------------ | ----------- | -------- | ----------------------------- |
| `tool_names` | `list[str]` | Yes      | List of tool names to exclude |

**Returns:**

| Type         | Description                                                 |
| ------------ | ----------------------------------------------------------- |
| `list[Tool]` | Filtered list of Tool objects excluding the specified names |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def tags(self, tags: list[str]) -> list[Tool]
```

**Parameters:**

| Name   | Type        | Required | Description                                                                     |
| ------ | ----------- | -------- | ------------------------------------------------------------------------------- |
| `tags` | `list[str]` | Yes      | List of tags to filter by (OR logic - tool must have at least one matching tag) |

**Returns:**

| Type         | Description                                                                |
| ------------ | -------------------------------------------------------------------------- |
| `list[Tool]` | Filtered list of Tool objects that have at least one of the specified tags |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def withoutTags(self, tags: list[str]) -> list[Tool]
```

**Parameters:**

| Name   | Type        | Required | Description                                                                      |
| ------ | ----------- | -------- | -------------------------------------------------------------------------------- |
| `tags` | `list[str]` | Yes      | List of tags to exclude (OR logic - tool is excluded if it has any matching tag) |

**Returns:**

| Type         | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `list[Tool]` | Filtered list of Tool objects that do NOT have any of the specified tags |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def toolsets(self, toolset_names: list[str]) -> list[Tool]
```

**Parameters:**

| Name            | Type        | Required | Description                                                                                         |
| --------------- | ----------- | -------- | --------------------------------------------------------------------------------------------------- |
| `toolset_names` | `list[str]` | Yes      | List of toolset names to include (OR logic - tool is included if it came from any matching toolset) |

**Returns:**

| Type         | Description                                               |
| ------------ | --------------------------------------------------------- |
| `list[Tool]` | Filtered list of Tool objects from the specified toolsets |

**Example:**

```python theme={null}
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:**

```python theme={null}
[
    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:**

```python theme={null}
def execute(self, tool_name: str, properties: dict[str, Any] | None = None) -> ExecutionResult
```

**Parameters:**

| Name         | Type             | Required | Description                                               |
| ------------ | ---------------- | -------- | --------------------------------------------------------- |
| `tool_name`  | `str`            | Yes      | Name of the tool to execute                               |
| `properties` | `dict[str, Any]` | No       | Properties/parameters to pass to the tool (default: `{}`) |

**Returns:**

| Type              | Description                                         |
| ----------------- | --------------------------------------------------- |
| `ExecutionResult` | Result object with success/error status and content |

**Raises:**

* `MCIClientError` - If tool not found or execution fails with validation error

**Example:**

```python theme={null}
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:**

```python theme={null}
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:**

```python theme={null}
# Raises MCIClientError
MCIClientError: Tool not found: invalid_tool_name
```

**Error Response - Execution Error:**

```python theme={null}
ExecutionResult(
    isError=True,
    content=None,
    error="HTTP request failed: 404 Not Found",
    metadata={
        "status_code": 404,
        "execution_time_ms": 75
    }
)
```

**Error Response - Network Error:**

```python theme={null}
ExecutionResult(
    isError=True,
    content=None,
    error="Connection timeout after 5000ms",
    metadata=None
)
```

***

#### `list_tools()`

List available tool names as strings.

**Method Signature:**

```python theme={null}
def list_tools(self) -> list[str]
```

**Parameters:**

None

**Returns:**

| Type        | Description                  |
| ----------- | ---------------------------- |
| `list[str]` | List of tool names (strings) |

**Example:**

```python theme={null}
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:**

```python theme={null}
["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:**

```python theme={null}
def get_tool_schema(self, tool_name: str) -> dict[str, Any]
```

**Parameters:**

| Name        | Type  | Required | Description      |
| ----------- | ----- | -------- | ---------------- |
| `tool_name` | `str` | Yes      | Name of the tool |

**Returns:**

| Type             | Description                                                      |
| ---------------- | ---------------------------------------------------------------- |
| `dict[str, Any]` | Tool's input schema as a dictionary (or empty dict if no schema) |

**Raises:**

* `MCIClientError` - If tool not found

**Example:**

```python theme={null}
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:**

```python theme={null}
{
    "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:**

```python theme={null}
{}  # Empty dict if tool has no input schema
```

**Error Response:**

```python theme={null}
# Raises MCIClientError
MCIClientError: Tool not found: invalid_tool_name
```

***

## Data Models

### MCISchema

Top-level MCI schema representing the complete MCI context file.

**Fields:**

| Field           | Type         | Required | Description                                 |
| --------------- | ------------ | -------- | ------------------------------------------- |
| `schemaVersion` | `str`        | Yes      | Schema version (e.g., "1.0")                |
| `metadata`      | `Metadata`   | No       | Optional metadata about the tool collection |
| `tools`         | `list[Tool]` | Yes      | List of tool definitions                    |

**Example:**

```python theme={null}
{
    "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:**

| Field         | Type                                                                                            | Required | Description                                               |
| ------------- | ----------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------- |
| `name`        | `str`                                                                                           | Yes      | Unique identifier for the tool                            |
| `annotations` | `Annotations`                                                                                   | No       | Tool metadata and hints                                   |
| `disabled`    | `bool`                                                                                          | No       | If true, tool is ignored (default: false)                 |
| `description` | `str`                                                                                           | No       | Description of what the tool does                         |
| `inputSchema` | `dict[str, Any]`                                                                                | No       | JSON Schema defining expected input properties            |
| `execution`   | `HTTPExecutionConfig` \| `CLIExecutionConfig` \| `FileExecutionConfig` \| `TextExecutionConfig` | Yes      | Execution configuration (determines how tool is executed) |

**Example:**

```python theme={null}
{
    "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:**

| Field      | Type             | Required | Description                                                   |
| ---------- | ---------------- | -------- | ------------------------------------------------------------- |
| `isError`  | `bool`           | Yes      | Whether an error occurred during execution                    |
| `content`  | `Any`            | No       | Result content (None if error)                                |
| `error`    | `str`            | No       | Error message (None if success)                               |
| `metadata` | `dict[str, Any]` | No       | Additional metadata (e.g., status\_code, execution\_time\_ms) |

**Example - Success:**

```python theme={null}
ExecutionResult(
    isError=False,
    content={
        "location": "New York",
        "temperature": 72,
        "conditions": "Sunny"
    },
    error=None,
    metadata={
        "status_code": 200,
        "execution_time_ms": 150
    }
)
```

**Example - Error:**

```python theme={null}
ExecutionResult(
    isError=True,
    content=None,
    error="HTTP request failed: 404 Not Found",
    metadata={
        "status_code": 404,
        "execution_time_ms": 75
    }
)
```

**Example - Text Content:**

```python theme={null}
ExecutionResult(
    isError=False,
    content="Hello, World!",
    error=None,
    metadata=None
)
```

**Example - File Content:**

```python theme={null}
ExecutionResult(
    isError=False,
    content="File content with template: user@example.com",
    error=None,
    metadata=None
)
```

***

### Metadata

Optional metadata about the MCI tool collection.

**Fields:**

| Field         | Type        | Required | Description                    |
| ------------- | ----------- | -------- | ------------------------------ |
| `name`        | `str`       | No       | Name of the tool collection    |
| `description` | `str`       | No       | Description of the collection  |
| `version`     | `str`       | No       | Version number (e.g., "1.0.0") |
| `license`     | `str`       | No       | License type (e.g., "MIT")     |
| `authors`     | `list[str]` | No       | List of author names           |

**Example:**

```python theme={null}
{
    "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:**

| Field        | Type             | Required | Default  | Description                                |
| ------------ | ---------------- | -------- | -------- | ------------------------------------------ |
| `type`       | `ExecutionType`  | Yes      | `"http"` | Execution type identifier                  |
| `method`     | `str`            | No       | `"GET"`  | HTTP method (GET, POST, PUT, DELETE, etc.) |
| `url`        | `str`            | Yes      | -        | URL endpoint for the request               |
| `headers`    | `dict[str, str]` | No       | `None`   | HTTP headers                               |
| `auth`       | `AuthConfig`     | No       | `None`   | Authentication configuration               |
| `params`     | `dict[str, Any]` | No       | `None`   | Query parameters                           |
| `body`       | `HTTPBodyConfig` | No       | `None`   | Request body configuration                 |
| `timeout_ms` | `int`            | No       | `30000`  | Request timeout in milliseconds            |
| `retries`    | `RetryConfig`    | No       | `None`   | Retry configuration                        |

**Example - GET Request:**

```python theme={null}
{
    "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:**

```python theme={null}
{
    "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:**

| Field        | Type                    | Required | Default | Description                             |
| ------------ | ----------------------- | -------- | ------- | --------------------------------------- |
| `type`       | `ExecutionType`         | Yes      | `"cli"` | Execution type identifier               |
| `command`    | `str`                   | Yes      | -       | Command to execute                      |
| `args`       | `list[str]`             | No       | `None`  | Command arguments                       |
| `flags`      | `dict[str, FlagConfig]` | No       | `None`  | Command flags configuration             |
| `cwd`        | `str`                   | No       | `None`  | Working directory for command execution |
| `timeout_ms` | `int`                   | No       | `30000` | Execution timeout in milliseconds       |

**Example - Simple Command:**

```python theme={null}
{
    "type": "cli",
    "command": "ls",
    "args": ["-la", "/home/user"],
    "timeout_ms": 5000
}
```

**Example - Command with Flags:**

```python theme={null}
{
    "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:**

| Field              | Type            | Required | Default  | Description                                              |
| ------------------ | --------------- | -------- | -------- | -------------------------------------------------------- |
| `type`             | `ExecutionType` | Yes      | `"file"` | Execution type identifier                                |
| `path`             | `str`           | Yes      | -        | Path to the file to read                                 |
| `enableTemplating` | `bool`          | No       | `True`   | Whether to process template placeholders in file content |

**Example - Read File with Templating:**

```python theme={null}
{
    "type": "file",
    "path": "/home/user/templates/email.txt",
    "enableTemplating": true
}
```

**Example - Read File Without Templating:**

```python theme={null}
{
    "type": "file",
    "path": "/home/user/data/config.json",
    "enableTemplating": false
}
```

***

### TextExecutionConfig

Configuration for simple text template execution.

**Fields:**

| Field  | Type            | Required | Default  | Description                            |
| ------ | --------------- | -------- | -------- | -------------------------------------- |
| `type` | `ExecutionType` | Yes      | `"text"` | Execution type identifier              |
| `text` | `str`           | Yes      | -        | Text template with placeholder support |

**Example:**

```python theme={null}
{
    "type": "text",
    "text": "Hello {{props.name}}! Your email is {{env.USER_EMAIL}}."
}
```

**Execution Result:**

```python theme={null}
# 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:**

| Field   | Type  | Required | Default    | Description                                 |
| ------- | ----- | -------- | ---------- | ------------------------------------------- |
| `type`  | `str` | Yes      | `"apiKey"` | Authentication type                         |
| `in`    | `str` | Yes      | -          | Where to place the key: "header" or "query" |
| `name`  | `str` | Yes      | -          | Name of the header or query parameter       |
| `value` | `str` | Yes      | -          | API key value (supports templates)          |

**Example - Header-based:**

```python theme={null}
{
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key",
    "value": "{{env.API_KEY}}"
}
```

**Example - Query parameter:**

```python theme={null}
{
    "type": "apiKey",
    "in": "query",
    "name": "api_key",
    "value": "{{env.API_KEY}}"
}
```

***

### BearerAuth

Bearer token authentication configuration.

**Fields:**

| Field   | Type  | Required | Default    | Description                             |
| ------- | ----- | -------- | ---------- | --------------------------------------- |
| `type`  | `str` | Yes      | `"bearer"` | Authentication type                     |
| `token` | `str` | Yes      | -          | Bearer token value (supports templates) |

**Example:**

```python theme={null}
{
    "type": "bearer",
    "token": "{{env.BEARER_TOKEN}}"
}
```

***

### BasicAuth

Basic authentication (username/password) configuration.

**Fields:**

| Field      | Type  | Required | Default   | Description                   |
| ---------- | ----- | -------- | --------- | ----------------------------- |
| `type`     | `str` | Yes      | `"basic"` | Authentication type           |
| `username` | `str` | Yes      | -         | Username (supports templates) |
| `password` | `str` | Yes      | -         | Password (supports templates) |

**Example:**

```python theme={null}
{
    "type": "basic",
    "username": "{{env.USERNAME}}",
    "password": "{{env.PASSWORD}}"
}
```

***

### OAuth2Auth

OAuth2 authentication configuration.

**Fields:**

| Field          | Type        | Required | Default    | Description                                  |
| -------------- | ----------- | -------- | ---------- | -------------------------------------------- |
| `type`         | `str`       | Yes      | `"oauth2"` | Authentication type                          |
| `flow`         | `str`       | Yes      | -          | OAuth2 flow type (e.g., "clientCredentials") |
| `tokenUrl`     | `str`       | Yes      | -          | Token endpoint URL                           |
| `clientId`     | `str`       | Yes      | -          | OAuth2 client ID                             |
| `clientSecret` | `str`       | Yes      | -          | OAuth2 client secret (supports templates)    |
| `scopes`       | `list[str]` | No       | `None`     | Optional OAuth2 scopes                       |

**Example:**

```python theme={null}
{
    "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:**

```python theme={null}
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:**

| Field      | Description                       |
| ---------- | --------------------------------- |
| `isError`  | Always `True` for errors          |
| `content`  | Always `None` for errors          |
| `error`    | Human-readable error message      |
| `metadata` | Optional additional error context |

**Example Error Scenarios:**

**HTTP Request Failed:**

```python theme={null}
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:**

```python theme={null}
result = client.execute("slow_api", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="Connection timeout after 30000ms",
#     metadata=None
# )
```

**CLI Command Failed:**

```python theme={null}
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:**

```python theme={null}
result = client.execute("read_config", {})
# ExecutionResult(
#     isError=True,
#     content=None,
#     error="File not found: /path/to/config.json",
#     metadata=None
# )
```

**Template Variable Missing:**

```python theme={null}
# 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:**

```python theme={null}
# 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):

   ```json theme={null}
   {
     "schemaVersion": "1.0",
     "enableAnyPaths": false,
     "directoryAllowList": ["/home/user/data", "./configs"],
     "tools": [...]
   }
   ```

2. **Tool-level settings** (overrides schema-level):
   ```json theme={null}
   {
     "name": "read_system_file",
     "enableAnyPaths": true,
     "execution": {
       "type": "file",
       "path": "{{props.file_path}}"
     }
   }
   ```

**Path Validation Behavior:**

| Scenario                                  | Allowed?     |
| ----------------------------------------- | ------------ |
| 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:**

```python theme={null}
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:**

```python theme={null}
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:**

```python theme={null}
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:

```python theme={null}
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:**

```json theme={null}
{
  "execution": {
    "type": "http",
    "url": "https://api.example.com/users/{{props.userId}}",
    "headers": {
      "Authorization": "Bearer {{env.API_TOKEN}}"
    }
  }
}
```

With execution:

```python theme={null}
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:**

| Name      | Type               | Required | Description                                                  |
| --------- | ------------------ | -------- | ------------------------------------------------------------ |
| `type`    | `Literal["stdio"]` | Yes      | Must be "stdio"                                              |
| `command` | `str`              | Yes      | Command to run (e.g., "uvx", "npx")                          |
| `args`    | `List[str]`        | No       | Arguments to pass to the command (default: `[]`)             |
| `env`     | `Dict[str, str]`   | No       | Environment variables for the server process (default: `{}`) |

**Example:**

```python theme={null}
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:**

| Name      | Type              | Required | Description                                                                 |
| --------- | ----------------- | -------- | --------------------------------------------------------------------------- |
| `type`    | `Literal["http"]` | Yes      | Must be "http"                                                              |
| `url`     | `HttpUrl`         | Yes      | Server URL (e.g., "[http://localhost:8000/mcp](http://localhost:8000/mcp)") |
| `headers` | `Dict[str, str]`  | No       | HTTP headers for authentication (default: `{}`)                             |

**Example:**

```python theme={null}
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:**

| Name              | Type                 | Required | Description                                |
| ----------------- | -------------------- | -------- | ------------------------------------------ |
| `server`          | `StdioCfg \| SseCfg` | Yes      | Server connection configuration            |
| `request_timeout` | `Optional[float]`    | No       | Request timeout in seconds (default: 60.0) |

**Example:**

```python theme={null}
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:**

| Name  | Type        | Required | Description                                                        |
| ----- | ----------- | -------- | ------------------------------------------------------------------ |
| `cfg` | `ClientCfg` | Yes      | Client configuration specifying server type and connection details |

**Example:**

```python theme={null}
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:**

```python theme={null}
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:**

```python theme={null}
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:**

| Name          | Type  | Required | Description                           |
| ------------- | ----- | -------- | ------------------------------------- |
| `name`        | `str` | Yes      | Name of the tool to call              |
| `**arguments` | `Any` | No       | Keyword 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:**

```python theme={null}
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)

```python theme={null}
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)

```python theme={null}
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

```python theme={null}
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:

```python theme={null}
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:

```python theme={null}
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
