Skip to main content

Basic Usage

This guide covers the essential patterns for working with the MCI Python adapter, from client initialization to tool execution and error handling.

Importing the Client

from mcipy import MCIClient

Creating Tool Schema Files

MCI supports both JSON and YAML formats for schema files. Choose the format that best suits your preferences.

JSON Format

Create a file named my-tools.mci.json:
{
  "schemaVersion": "1.0",
  "metadata": {
    "name": "My Tools",
    "description": "A collection of useful tools",
    "version": "1.0.0"
  },
  "tools": [
    {
      "name": "greet_user",
      "annotations": {
        "title": "User Greeting",
        "readOnlyHint": true,
        "idempotentHint": true
      },
      "description": "Generate a personalized greeting message",
      "inputSchema": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "The user's name"
          }
        },
        "required": ["username"]
      },
      "execution": {
        "type": "text",
        "text": "Hello, {{props.username}}! Welcome to MCI."
      }
    }
  ]
}

YAML Format

Create a file named my-tools.mci.yaml:
schemaVersion: "1.0"
metadata:
  name: My Tools
  description: A collection of useful tools
  version: 1.0.0
tools:
  - name: greet_user
    annotations:
      title: User Greeting
      readOnlyHint: true
      idempotentHint: true
    description: Generate a personalized greeting message
    inputSchema:
      type: object
      properties:
        username:
          type: string
          description: The user's name
      required:
        - username
    execution:
      type: text
      text: Hello, {{props.username}}! Welcome to MCI.
Note: MCI supports both JSON (.json) and YAML (.yaml, .yml) formats interchangeably.

Initializing the Client

Basic Initialization

from mcipy import MCIClient

# Initialize with JSON schema file
client = MCIClient(schema_file_path="my-tools.mci.json")

# Initialize with YAML schema file
client = MCIClient(schema_file_path="my-tools.mci.yaml")

With Environment Variables

client = MCIClient(
    schema_file_path="my-tools.mci.json",
    env_vars={
        "API_KEY": "your-secret-key",
        "USERNAME": "demo_user",
        "DATABASE_URL": "postgresql://localhost/mydb"
    }
)

Backward Compatibility

client = MCIClient(
    schema_file_path="my-tools.mci.json",
    env_vars={"API_KEY": "your-secret-key"}
)

Working with Tools

Listing Tools

Get a list of all available tool names:
tool_names = client.list_tools()
print(f"Available tools: {tool_names}")
# Output: ['greet_user', 'get_weather', 'create_report']
Get full tool objects with metadata:
tools = client.tools()
for tool in tools:
    title = tool.annotations.title if tool.annotations else tool.name
    print(f"- {tool.name}: {title}")
# Output:
# - greet_user: User Greeting
# - get_weather: Get Weather Information
# - create_report: Create Report

Executing Tools

Execute a tool with properties:
result = client.execute(
    tool_name="greet_user",
    properties={"username": "Alice"}
)

# Check the result
if result.result.isError:
    print(f"Error: {result.result.content[0].text}")
else:
    print(f"Success: {result.result.content[0].text}")
Execute without properties (if not required):
result = client.execute(tool_name="get_system_info")

Filtering Tools

By Tool Names (Include Only)

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

By Tool Names (Exclude)

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

By Tags

# Include tools with specific tags
api_tools = client.tags(["api", "external"])

# Exclude tools with specific tags
internal_tools = client.withoutTags(["external", "deprecated"])

By Toolsets

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

Getting Tool Schemas

Retrieve the input schema for a tool:
schema = client.get_tool_schema("greet_user")
print(f"Required properties: {schema.get('required', [])}")
print(f"Properties: {list(schema.get('properties', {}).keys())}")

# Output:
# Required properties: ['username']
# Properties: ['username']

Execution Types

MCI supports four execution types: Text, File, CLI, and HTTP. Each type is designed for different use cases.

Text Execution

Return static or templated text directly. Perfect for simple messages, templates, or computed strings. Schema Example:
{
  "name": "generate_welcome",
  "description": "Generate a welcome message with current date",
  "inputSchema": {
    "type": "object",
    "properties": {
      "username": {
        "type": "string",
        "description": "User's name"
      }
    },
    "required": ["username"]
  },
  "execution": {
    "type": "text",
    "text": "Welcome {{props.username}}! Today is {{env.CURRENT_DATE}}."
  }
}
Python Usage:
from datetime import datetime

client = MCIClient(
    schema_file_path="tools.mci.json",
    env_vars={"CURRENT_DATE": datetime.now().strftime("%Y-%m-%d")}
)

result = client.execute(
    tool_name="generate_welcome",
    properties={"username": "Alice"}
)
print(result.result.content[0].text)
# Output: "Welcome Alice! Today is 2024-01-15."

File Execution

Read and return file contents with optional template substitution. Useful for loading configuration files, templates, or documentation. Schema Example:
{
  "name": "load_config",
  "description": "Load a configuration file with template substitution",
  "inputSchema": {
    "type": "object",
    "properties": {
      "config_name": {
        "type": "string",
        "description": "Name of the configuration"
      }
    },
    "required": ["config_name"]
  },
  "execution": {
    "type": "file",
    "path": "./configs/{{props.config_name}}.conf",
    "enableTemplating": true
  }
}
File Content (configs/database.conf):
host={{env.DB_HOST}}
port={{env.DB_PORT}}
user={{env.DB_USER}}
database={{props.database_name}}
Python Usage:
client = MCIClient(
    schema_file_path="tools.mci.json",
    env_vars={
        "DB_HOST": "localhost",
        "DB_PORT": "5432",
        "DB_USER": "admin"
    }
)

result = client.execute(
    tool_name="load_config",
    properties={
        "config_name": "database",
        "database_name": "production_db"
    }
)
print(result.result.content[0].text)
# Output:
# host=localhost
# port=5432
# user=admin
# database=production_db

CLI Execution

Execute command-line programs and capture their output. Great for running system commands, scripts, or CLI tools. Schema Example:
{
  "name": "search_files",
  "description": "Search for text patterns in files",
  "inputSchema": {
    "type": "object",
    "properties": {
      "pattern": {
        "type": "string",
        "description": "Search pattern"
      },
      "directory": {
        "type": "string",
        "description": "Directory to search"
      },
      "ignore_case": {
        "type": "boolean",
        "description": "Ignore case when searching"
      }
    },
    "required": ["pattern", "directory"]
  },
  "execution": {
    "type": "cli",
    "command": "grep",
    "args": ["-r", "-n"],
    "flags": {
      "-i": {
        "from": "props.ignore_case",
        "type": "boolean"
      }
    },
    "cwd": "{{props.directory}}",
    "timeout_ms": 8000
  }
}
Python Usage:
client = MCIClient(schema_file_path="tools.mci.json")

result = client.execute(
    tool_name="search_files",
    properties={
        "pattern": "TODO",
        "directory": "./src",
        "ignore_case": True
    }
)

if result.result.isError:
    print(f"Error: {result.result.content[0].text}")
else:
    print(result.result.content[0].text)  # Output from grep command
CLI Configuration Options:
  • command: The command to execute (e.g., “grep”, “python”, “node”)
  • args: Fixed arguments passed to the command
  • flags: Dynamic flags based on input properties
    • type: "boolean": Include flag only if property is true
    • type: "value": Include flag with property value (e.g., --file value)
  • cwd: Working directory for command execution
  • timeout_ms: Maximum execution time in milliseconds

HTTP Execution

Make HTTP requests to APIs with full support for authentication, headers, query parameters, and request bodies.

Basic GET Request

{
  "name": "get_weather",
  "description": "Fetch current weather for a location",
  "inputSchema": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City name"
      }
    },
    "required": ["location"]
  },
  "execution": {
    "type": "http",
    "method": "GET",
    "url": "https://api.example.com/weather",
    "params": {
      "location": "{{props.location}}",
      "units": "metric"
    },
    "headers": {
      "Accept": "application/json"
    },
    "timeout_ms": 5000
  }
}

POST Request with JSON Body

{
  "name": "create_report",
  "description": "Create a new report via API",
  "inputSchema": {
    "type": "object",
    "properties": {
      "title": { "type": "string" },
      "content": { "type": "string" }
    },
    "required": ["title", "content"]
  },
  "execution": {
    "type": "http",
    "method": "POST",
    "url": "https://api.example.com/reports",
    "headers": {
      "Content-Type": "application/json"
    },
    "body": {
      "type": "json",
      "content": {
        "title": "{{props.title}}",
        "content": "{{props.content}}",
        "timestamp": "{{env.CURRENT_TIMESTAMP}}"
      }
    },
    "timeout_ms": 10000
  }
}

Authentication Types

API Key (Header):
{
  "auth": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key",
    "value": "{{env.API_KEY}}"
  }
}
API Key (Query Parameter):
{
  "auth": {
    "type": "apiKey",
    "in": "query",
    "name": "api_key",
    "value": "{{env.API_KEY}}"
  }
}
Bearer Token:
{
  "auth": {
    "type": "bearer",
    "token": "{{env.BEARER_TOKEN}}"
  }
}
Basic Authentication:
{
  "auth": {
    "type": "basic",
    "username": "{{env.USERNAME}}",
    "password": "{{env.PASSWORD}}"
  }
}
OAuth2:
{
  "auth": {
    "type": "oauth2",
    "flow": "clientCredentials",
    "tokenUrl": "https://auth.example.com/token",
    "clientId": "{{env.CLIENT_ID}}",
    "clientSecret": "{{env.CLIENT_SECRET}}",
    "scopes": ["read:data"]
  }
}

Python Usage Example

from datetime import datetime

client = MCIClient(
    schema_file_path="api-tools.mci.json",
    env_vars={
        "API_KEY": "your-secret-key",
        "BEARER_TOKEN": "your-bearer-token",
        "CURRENT_TIMESTAMP": datetime.now().isoformat()
    }
)

# Execute GET request
weather_result = client.execute(
    tool_name="get_weather",
    properties={"location": "New York"}
)

if not weather_result.result.isError:
    print(f"Weather data: {weather_result.result.content[0].text}")

# Execute POST request
report_result = client.execute(
    tool_name="create_report",
    properties={
        "title": "Q1 Sales Report",
        "content": "Sales increased by 15%"
    }
)

if not report_result.result.isError:
    print(f"Report created: {report_result.result.content[0].text}")

Advanced Features

Toolsets

Toolsets allow you to organize tools into reusable, modular collections. See the Toolsets Concept Guide for detailed information. Quick Example:
{
  "schemaVersion": "1.0",
  "libraryDir": "./mci",
  "toolsets": [
    { "name": "weather" },
    {
      "name": "database",
      "filter": "withoutTags",
      "filterValue": "destructive"
    }
  ]
}
client = MCIClient(schema_file_path="main.mci.json")

# Get tools from specific toolsets
weather_tools = client.toolsets(["weather"])

Error Handling

Always check the isError property of execution results:
result = client.execute(tool_name="my_tool", properties={...})

if result.result.isError:
    print(f"Error occurred: {result.result.content[0].text}")
    # Handle error case
    if result.metadata:
        print(f"Additional context: {result.metadata}")
else:
    print(f"Success: {result.result.content[0].text}")
    # Process successful result

Multiple Clients

You can create multiple client instances for different schema files:
# Client for API tools
api_client = MCIClient(
    schema_file_path="api-tools.mci.json",
    env_vars={"API_KEY": "key1"}
)

# Client for CLI tools
cli_client = MCIClient(
    schema_file_path="cli-tools.mci.json",
    env_vars={"WORKSPACE": "/home/user"}
)

Environment Variables

Environment variables are the recommended way to handle secrets and configuration:
import os

client = MCIClient(
    schema_file_path="tools.mci.json",
    env_vars={
        "API_KEY": os.getenv("MY_API_KEY"),
        "DATABASE_URL": os.getenv("DATABASE_URL"),
        "ENVIRONMENT": "production"
    }
)

Security: Path Restrictions

Important Security Feature: By default, MCI restricts file and directory access to protect against arbitrary file access vulnerabilities.

Default Behavior

When executing file-based tools or CLI tools with a working directory (cwd), MCI validates that all paths are within the directory containing the schema file:
# This works - accessing file in schema directory
client = MCIClient(schema_file_path="/project/tools.mci.json")
result = client.execute("read_config", {"file": "/project/config.json"})
# ✓ Allowed: /project/config.json is in same directory as schema
# This fails - accessing file outside schema directory
result = client.execute("read_secret", {"file": "/etc/passwd"})
# ✗ Blocked: Path outside schema directory

Allowing Specific Directories

You can allow additional directories using directoryAllowList:
{
  "schemaVersion": "1.0",
  "directoryAllowList": ["/home/user/data", "./configs"],
  "tools": [...]
}

Per-Tool Configuration

Override security settings for individual tools:
{
  "name": "read_any_file",
  "enableAnyPaths": true,
  "execution": {
    "type": "file",
    "path": "{{props.file_path}}"
  }
}
Important Notes:
  1. Tool-level settings override schema-level settings
  2. Relative paths are resolved relative to the schema directory
  3. enableAnyPaths disables all path validation - Use with extreme caution
  4. Subdirectories are automatically allowed

Complete Example

Here’s a complete example putting it all together:
#!/usr/bin/env python3
"""
Complete MCI example with multiple execution types.
"""

from datetime import datetime
from mcipy import MCIClient

def main():
    # Initialize client with environment variables
    client = MCIClient(
        schema_file_path="./tools.mci.json",
        env_vars={
            "CURRENT_DATE": datetime.now().strftime("%Y-%m-%d"),
            "API_KEY": "demo-api-key-123",
            "USERNAME": "demo_user"
        }
    )

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

    # Execute text tool
    print("\n1. Executing text tool...")
    result = client.execute(
        tool_name="generate_welcome",
        properties={"username": "Alice"}
    )
    if not result.result.isError:
        print(f"   Output: {result.result.content[0].text}")

    # Execute file tool
    print("\n2. Executing file tool...")
    result = client.execute(
        tool_name="load_config",
        properties={"config_name": "database"}
    )
    if not result.result.isError:
        print(f"   Config loaded: {len(result.result.content[0].text)} bytes")

    # Execute CLI tool
    print("\n3. Executing CLI tool...")
    result = client.execute(
        tool_name="search_files",
        properties={
            "pattern": "TODO",
            "directory": ".",
            "ignore_case": True
        }
    )
    if not result.result.isError:
        print(f"   Found matches: {len(result.result.content[0].text.splitlines())} lines")

    # Filter tools
    print("\n4. Filtering tools...")
    text_tools = client.only(["generate_welcome"])
    print(f"   Filtered to {len(text_tools)} tools")

    print("\n✓ Example completed successfully!")

if __name__ == "__main__":
    main()