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