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:
- vMCP calls
filesystem.read_filewith{"path": "/logs/error.log"} - Gets result:
"Error at line 42..." - Substitutes into issue body
- Calls
github.create_issuewith 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 queryresource_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