Skip to main content

# Variable Substitution

vMCP's powerful variable substitution system allows dynamic tool arguments and configuration reuse.

## Overview​

Variable substitution patterns:

- `@param:name` - Runtime parameters from active_config
- `@config:key` - Configuration values
- `@tool(server.tool_name)` - Call other tools inline
- `@resource(server.uri)` - Read resource content
- `@prompt(server.prompt_name)` - Use prompt templates

## @param - Runtime Parameters​

Use values from the vMCP's `active_config` at runtime.

### Example Setup​

vMCP Active Config:

    {
      "repo": "myorg/myapp",
      "branch": "main",
      "workspace": "/home/user/projects/myapp"
    }

Tool Call:

    {
      "name": "github.get_file_contents",
      "arguments": {
        "repo": "@param:repo",
        "branch": "@param:branch",
        "path": "README.md"
      }
    }

Resolved to:

    {
      "name": "github.get_file_contents",
      "arguments": {
        "repo": "myorg/myapp",
        "branch": "main",
        "path": "README.md"
      }
    }

### Use Cases​

- API Keys: `@param:api_key`
- Default Paths: `@param:workspace`
- User Preferences: `@param:default_language`
- Project Settings: `@param:repo_name`

## @config - Configuration Values​

Reference static configuration values.

vMCP Config:

    {
      "database_url": "postgresql://localhost/mydb",
      "max_retries": 3,
      "timeout": 30
    }

Tool Call:

    {
      "name": "postgres.query",
      "arguments": {
        "connection": "@config:database_url",
        "query": "SELECT * FROM users"
      }
    }

## @tool() - Call Other Tools​

Execute another tool and use its result as an argument.

### Example: Chaining Tools​

Read a file, then create a GitHub issue with its contents:

    {
      "name": "github.create_issue",
      "arguments": {
        "repo": "@param:repo",
        "title": "Bug Report",
        "body": "@tool(filesystem.read_file, {\"path\": \"/logs/error.log\"})"
      }
    }

Execution Flow:

1. vMCP calls `filesystem.read_file` with `{"path": "/logs/error.log"}`
2. Gets result: `"Error at line 42..."`
3. Substitutes into issue body
4. Calls `github.create_issue` with resolved body

### Nested Tool Calls​

You can nest tool calls:

    {
      "name": "send_notification",
      "arguments": {
        "message": "@tool(format_message, {\"content\": \"@tool(get_latest_commit)\"})"
      }
    }

### Error Handling​

If a tool call fails, the substitution fails and the original call is aborted:

    {
      "error": "Tool substitution failed: filesystem.read_file - File not found"
    }

## @resource() - Read Resources​

Read content from MCP resources.

### Example: File Resources​

    {
      "name": "analyze_code",
      "arguments": {
        "code": "@resource(filesystem:file:///src/main.py)"
      }
    }

### Example: Database Resources​

    {
      "name": "generate_report",
      "arguments": {
        "data": "@resource(postgres:query://SELECT * FROM sales)"
      }
    }

### Resource URI Format​

    {server_name}:{resource_type}://{resource_path}

- `server_name` - Which MCP server to query
- `resource_type` - Type of resource (file, query, etc.)
- `resource_path` - Path or identifier

## @prompt() - Prompt Templates​

Use pre-defined prompt templates from MCP servers.

### Example: Code Review Template​

Available Prompt:

    {
      "name": "code_review",
      "description": "Template for code review",
      "arguments": {
        "language": "string",
        "style": "string"
      }
    }

Tool Call:

    {
      "name": "github.create_issue",
      "arguments": {
        "repo": "@param:repo",
        "title": "Code Review",
        "body": "@prompt(prompts.code_review, {\"language\": \"python\", \"style\": \"detailed\"})"
      }
    }

## Combining Substitutions​

You can combine multiple substitution types:

    {
      "name": "deploy_app",
      "arguments": {
        "repo": "@param:repo",
        "environment": "@config:default_environment",
        "version": "@tool(git.get_latest_tag)",
        "config_file": "@resource(filesystem:file:///config/prod.yml)",
        "notification": "@prompt(notifications.deploy_template, {\"env\": \"production\"})"
      }
    }

## Escaping Variables​

If you need literal `@param` text:

    {
      "text": "\\@param:not_a_variable"
    }

The `\\@` escapes the substitution.

## Best Practices​

### 1. Use @param for Dynamic Values​

    // Good: Flexible, reusable
    "repo": "@param:repo"

    // Bad: Hardcoded
    "repo": "myorg/myapp"

### 2. Use @config for Static Settings​

    // Good: Centralized configuration
    "timeout": "@config:api_timeout"

    // Bad: Magic numbers
    "timeout": 30000

### 3. Chain Tools for Complex Workflows​

    {
      "name": "create_pr",
      "arguments": {
        "diff": "@tool(git.diff, {\"branch\": \"@param:feature_branch\"})",
        "description": "@prompt(pr_template, {\"type\": \"feature\"})"
      }
    }

### 4. Validate Substitutions​

Always ensure:

- Parameters exist in active_config
- Tools exist and are accessible
- Resources are valid URIs
- Prompts accept the provided arguments

## Error Messages​

Common substitution errors:

    // Missing parameter
    {
      "error": "Parameter not found: @param:missing_key"
    }

    // Tool call failed
    {
      "error": "Tool substitution failed: server.tool_name - Connection refused"
    }

    // Invalid resource
    {
      "error": "Resource not found: @resource(invalid:uri)"
    }

    // Prompt error
    {
      "error": "Prompt missing required argument: language"
    }

## Advanced Examples​

### Example 1: Automated Testing​

    {
      "name": "run_tests",
      "arguments": {
        "test_files": "@tool(filesystem.list_directory, {\"path\": \"@param:test_dir\"})",
        "config": "@resource(filesystem:file:///@param:test_config)",
        "notification_webhook": "@config:slack_webhook"
      }
    }

### Example 2: Data Pipeline​

    {
      "name": "process_data",
      "arguments": {
        "input": "@resource(postgres:query://SELECT * FROM raw_data)",
        "transform": "@tool(get_transform_script)",
        "output": "@param:output_table",
        "schema": "@resource(filesystem:file:///schemas/output.json)"
      }
    }

### Example 3: Documentation Generation​

    {
      "name": "github.create_issue",
      "arguments": {
        "title": "Update Documentation",
        "body": "@prompt(docs.update_template, {\"version\": \"@tool(git.get_version)\"})",
        "labels": ["documentation"],
        "assignee": "@config:docs_maintainer"
      }
    }

## Performance Considerations​

- Tool substitutions add latency (each is a separate call)
- Resource reads may be cached by the MCP server
- Prompt generation is usually fast
- Parameter substitution has negligible overhead

Minimize nested tool calls for better performance:

    // Slower: 3 sequential calls
    "@tool(a, {\"arg\": \"@tool(b, {\"arg\": \"@tool(c)\"})\"})"

    // Faster: Parallel calls where possible

## Next Steps​

- Managing vMCPs
- API Reference
- Configuration Guide