Skip to content

MCP Server Templates

MCP Server Templates enable dynamic, context-aware server configuration. Instead of hardcoding server settings, you can define template configurations that automatically adapt based on runtime context like the current project, user, environment, or client connection.

Overview

Templates allow you to:

  • Dynamic server creation: Spawn different servers based on project context
  • Environment-aware configuration: Automatically adjust settings per environment
  • Context enrichment: Inject project-specific metadata into server configurations
  • Conditional enablement: Enable/disable servers based on runtime conditions

Templates vs. Static Servers

1MCP supports two types of server configurations:

FeatureStatic Servers (mcpServers)Template Servers (mcpTemplates)
ConfigurationFixed values at startupDynamic values based on context
Context awarenessNoneProject, user, transport, client
Multiple instancesSingle instance per configMultiple instances per context
LifecycleAlways runningCreated on-demand per connection
Use caseStable infrastructureDynamic, context-specific tools

Managing definitions in the Admin Console

The Admin Console's Configured servers workspace lists static and Template Server definitions together. A definition is identified by both its source and name: mcpServers/<name> for a static target and mcpTemplates/<name> for a Template Server definition. If both sources contain the same name, both rows remain available. The template definition is authoritative for runtime matching, while the static definition is marked shadowed.

Template detail describes the saved definition, not a live Template Server Instance. It shows normalized transport and lifecycle fields, redacted secret actions, Handlebars syntax status, referenced Request Context variable names, and a bounded active-instance count. Request Context values are never shown.

Create, edit, and rename use structured controls. Preview is structural only: it validates the configuration and the Handlebars fields that the runtime renders (command, args, env values, cwd, url, and disabled), reports a redacted change plan and active-instance impact, and does not render context, connect a backend, or create an instance. Create and rename fail if either source already owns the destination name, and apply requires the current preview.

Creating a definition only adds it to the template index after reload. An instance is still created lazily by a future matching request. Renaming a definition or changing structural/runtime-rendered fields retires its active instances after the successful reload; future matching requests create instances from the updated definition. Metadata-only changes to instructions, disabled tools, or tool descriptions retain live instances.

Full administrators can also disable or re-enable one source-qualified Template definition. The read-only preview binds the current definition fingerprint, requested state, reload plan, and current active-instance count. A context-rendered disabled expression is treated as operator-enabled; disabling it explicitly replaces the expression with literal true without evaluating or exposing Request Context. Re-enable removes disabled. A same-named static definition is never changed.

After a disable is persisted, retirement runs only after a successful reload. Every instance that was active before the write is retired, and its persistent and ephemeral Request Session memberships are removed; the Request Sessions themselves remain. The result reports persistence, reload, and observed retirement separately, so a durable write with a failed reload is not described as runtime success. Re-enable restores index eligibility but does not recreate old instances or memberships. A later matching request creates a new instance lazily. Repeating an already-satisfied lifecycle action is unchanged and skips backup, write, reload, and retirement.

Deletion starts with a read-only structural preview of exactly one source-qualified definition. The preview shows the redacted definition, authority or shadow state, same-name source preservation, required recovery copy, expected reload, and runtime impact. Apply requires typing the exact printable identity supplied by the server, such as mcpTemplates/workspace-worker; a bare name is never accepted. If the definition, source, or preview changes, deletion stops without writing. A successful Template definition deletion reloads the Runtime Scope and reports retirement of the instances that were active before the write. Presets, filters, credentials, registry state, the other source, and individual instance configuration are never deleted as a cascade.

Key Difference from Instruction Templates

MCP Server Templates (mcpTemplates) configure server instances with dynamic values like:

  • {{project.path}} - Project directory path
  • {{user.username}} - Current user

Instruction Templates customize LLM instructions with variables like:

  • {{serverCount}} - Number of connected servers
  • {{serverNames}} - List of server names

Quick Start

Basic Template Example

Add a template to your mcp.json:

json
{
  "mcpTemplates": {
    "project-context": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "{{project.path}}"],
      "tags": ["filesystem", "project"]
    }
  }
}

When a client connects, 1MCP:

  1. Collects context (project path, user, environment)
  2. Renders the template with actual values
  3. Creates a server instance with the rendered configuration
  4. Connects the client to the new instance

Environment-Specific Configuration

json
{
  "mcpTemplates": {
    "conditional-server": {
      "command": "node",
      "args": ["{{project.path}}/server.js"],
      "env": {
        "NODE_ENV": "{{project.environment}}",
        "DEBUG": "{{#if (eq project.environment 'development')}}true{{else}}false{{/if}}"
      },
      "disabled": "{{#if (eq project.environment 'production')}}true{{else}}false{{/if}}",
      "tags": ["development"]
    }
  }
}

Template Variables

Templates have access to four namespaces of context variables:

Project Variables (project.*)

VariableTypeDescriptionExample
project.pathstringAbsolute path to current project/Users/dev/myapp
project.namestringProject directory namemyapp
project.environmentstringEnvironment namedevelopment
project.git.branchstring?Git branch namemain
project.git.commitstring?Git commit hasha1b2c3d
project.git.repositorystring?Git remote URLhttps://github.com/user/repo
project.custom.*anyCustom values from .1mcprcUser-defined

User Variables (user.*)

VariableTypeDescriptionExample
user.usernamestring?System usernamedeveloper
user.namestring?User's full nameJane Developer
user.emailstring?User email[email protected]
user.homestring?Home directory path/Users/developer
user.uidstring?User ID501
user.gidstring?Group ID20
user.shellstring?Default shell/bin/zsh

Transport Variables (transport.*)

VariableTypeDescriptionExample
transport.typestringTransport protocolhttp, sse, stdio
transport.urlstring?Server URL (HTTP/SSE)http://localhost:3050
transport.connectionIdstring?Connection identifierconn_xyz789
transport.connectionTimestampstring?Connection time2025-01-25T10:30:00Z
transport.client.namestringClient application namecursor, claude-code
transport.client.versionstringClient version1.0.0
transport.client.titlestring?Client display nameCursor Editor

Template Syntax

1MCP uses Handlebars for template rendering. Variables use double curly braces: {{variable}}.

Variable Access

text
{{project.path}}              <!-- /Users/dev/project -->
{{user.username}}             <!-- developer -->
{{transport.client.name}}     <!-- cursor -->
{{project.custom.teamId}}     <!-- Custom value from .1mcprc -->

Conditionals

Use {{#if}} for conditional logic:

text

{{#if (eq project.environment 'production')}}
  <!-- Production configuration -->
{{else}}
  <!-- Development configuration -->
{{/if}}

Comparisons

Use built-in helpers for comparisons:

text
{{#if (eq project.environment 'development')}}{{/if}}
{{#if (ne user.username 'root')}}{{/if}}
{{#if (gt project.custom.count 5)}}{{/if}}
{{#if (lt transport.client.version '2.0')}}{{/if}}

Logical Operators

Combine conditions with and/or:

text
{{#if (and (eq project.environment 'production') (eq project.custom.region 'us'))}}
{{/if}}

{{#if (or (eq project.custom.team 'backend') (eq project.custom.team 'devops'))}}
{{/if}}

String Operations

text
{{#if (contains project.name 'admin')}}
{{/if}}

{{#if (startsWith project.git.branch 'feature/')}}
{{/if}}

{{#if (endsWith project.name '-test')}}
{{/if}}

Context Enrichment (.1mcprc)

Project-level context enrichment allows you to inject custom metadata into templates. Create a .1mcprc file in your project root:

json
{
  "preset": "my-team-preset",
  "tags": ["team-a", "backend"],
  "context": {
    "projectId": "myapp-backend",
    "environment": "development",
    "team": "platform",
    "custom": {
      "teamId": "team-a",
      "region": "us-west",
      "debugMode": true,
      "apiEndpoint": "https://dev-api.example.com"
    },
    "envPrefixes": ["MYAPP_*", "TEAM_*"],
    "includeGit": true,
    "sanitizePaths": true
  }
}

Context Fields

FieldTypeDescription
presetstringDefault preset to use
tagsstring|string[]Default tags for filtering
context.projectIdstringProject identifier
context.environmentstringEnvironment name (development, staging, production)
context.teamstringTeam name
context.customobjectCustom key-value pairs
context.envPrefixesstring[]Environment variable prefixes to include
context.includeGitbooleanInclude Git information
context.sanitizePathsbooleanSanitize file paths for security

Accessing Custom Context

Custom values are available as {{project.custom.*}}:

json
{
  "mcpTemplates": {
    "team-server": {
      "command": "npx",
      "args": ["-y", "serena", "{{project.path}}"],
      "env": {
        "TEAM_ID": "{{project.custom.teamId}}",
        "REGION": "{{project.custom.region}}",
        "API_ENDPOINT": "{{project.custom.apiEndpoint}}",
        "DEBUG": "{{#if project.custom.debugMode}}true{{else}}false{{/if}}"
      },
      "tags": ["{{project.custom.team}}"]
    }
  }
}

Complete Example

Here's a comprehensive template configuration:

json
{
  "$schema": "https://docs.1mcp.app/schemas/v1.0.0/mcp-config.json",
  "version": "1.0.0",
  "templateSettings": {
    "validateOnReload": true,
    "failureMode": "graceful",
    "cacheContext": true
  },
  "mcpTemplates": {
    "project-filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "{{project.path}}"],
      "tags": ["filesystem", "project-local"],
      "disabled": "{{#if (eq transport.client.name 'claude-code')}}false{{else}}true{{/if}}"
    },
    "team-serena": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/oraios/serena",
        "serena",
        "start-mcp-server",
        "--project",
        "{{project.path}}",
        "--team",
        "{{project.custom.team}}",
        "--env",
        "{{project.environment}}"
      ],
      "env": {
        "PROJECT_ID": "{{project.custom.projectId}}",
        "GIT_BRANCH": "{{project.git.branch}}",
        "API_ENDPOINT": "{{project.custom.apiEndpoint}}"
      },
      "cwd": "{{project.path}}",
      "tags": ["filesystem", "search"]
    },
    "conditional-debug-server": {
      "command": "node",
      "args": ["{{project.path}}/debug-server.js"],
      "cwd": "{{project.path}}",
      "env": {
        "NODE_ENV": "{{project.environment}}",
        "DEBUG": "{{#if (eq project.environment 'development')}}true{{else}}false{{/if}}",
        "LOG_LEVEL": "{{#if (eq project.environment 'production')}}warn{{else}}debug{{/if}}"
      },
      "disabled": "{{#if (eq project.environment 'production')}}true{{else}}false{{/if}}",
      "tags": ["debug", "development"]
    },
    "client-aware-server": {
      "command": "npx",
      "args": [
        "-y",
        "my-custom-server",
        "--client",
        "{{transport.client.name}}",
        "--version",
        "{{transport.client.version}}"
      ],
      "env": {
        "CLIENT_NAME": "{{transport.client.name}}",
        "CLIENT_VERSION": "{{transport.client.version}}",
        "CONNECTION_ID": "{{transport.connectionId}}",
        "USER": "{{user.username}}"
      },
      "tags": ["client-aware", "custom"]
    }
  }
}

Template Settings

Control template processing behavior with templateSettings:

json
{
  "templateSettings": {
    "validateOnReload": true,
    "failureMode": "graceful",
    "cacheContext": true
  }
}
SettingTypeDefaultDescription
validateOnReloadbooleanfalseReserved for future use; currently has no effect
failureMode'strict'|'graceful''strict'How to handle template errors
cacheContextbooleantrueCache rendered templates by context hash

Failure Modes

  • strict: Template errors cause the affected template server to be skipped; the error is logged. Other servers continue normally.
  • graceful: Template errors are logged, original template used as fallback

Conditional Disable

Templates can be conditionally disabled using the disabled field:

json
{
  "mcpTemplates": {
    "dev-only-server": {
      "command": "node",
      "args": ["dev-tools.js"],
      "disabled": "{{#if (eq project.environment 'production')}}true{{else}}false{{/if}}",
      "tags": ["development"]
    },
    "client-specific": {
      "command": "npx",
      "args": ["-y", "special-server"],
      "disabled": "{{#if (eq transport.client.name 'cursor')}}false{{else}}true{{/if}}",
      "tags": ["cursor-only"]
    },
    "user-restricted": {
      "command": "npx",
      "args": ["-y", "admin-tools"],
      "disabled": "{{#if (contains user.username 'admin')}}false{{else}}true{{/if}}",
      "tags": ["admin"]
    }
  }
}

The disabled field evaluates the template and converts the result to a boolean:

  • "true", "1", "yes"true (disabled)
  • "false", "0", "no" or empty → false (enabled)

Handlebars Helpers

1MCP includes several built-in Handlebars helpers:

Math Helpers

text
{{math value1 '+' value2}}           <!-- Addition -->
{{math value1 '-' value2}}           <!-- Subtraction -->
{{math value1 '*' value2}}           <!-- Multiplication -->
{{math value1 '/' value2}}           <!-- Division -->
{{math value1 '%' value2}}           <!-- Modulo -->
{{math value1 '**' value2}}          <!-- Exponentiation -->
{{math value '/' 100 '*' 100}}       <!-- Chained operations (all results rounded) -->

Comparison Helpers

text
{{eq a b}}     <!-- Equal -->
{{ne a b}}     <!-- Not equal -->
{{gt a b}}     <!-- Greater than -->
{{lt a b}}     <!-- Less than -->

Logical Helpers

text
{{and a b c}}  <!-- All truthy -->
{{or a b c}}   <!-- Any truthy -->

String Helpers

text
{{contains str substring}}     <!-- Contains substring -->
{{startsWith str prefix}}      <!-- Starts with prefix -->
{{endsWith str suffix}}        <!-- Ends with suffix -->
{{len str}}                    <!-- String length -->
{{substring str start end}}    <!-- Extract substring -->

Math Operation Helpers

text
{{subtract a b}}    <!-- a - b with null safety -->
{{div a b}}         <!-- a / b with zero safety -->

Best Practices

1. Use .1mcprc for Project Context

Store project-specific metadata in .1mcprc rather than hardcoding in templates:

Good (.1mcprc):

json
{
  "context": {
    "projectId": "myapp-api",
    "team": "platform",
    "custom": {
      "apiEndpoint": "https://api.example.com"
    }
  }
}

Avoid (hardcoded):

json
{
  "mcpTemplates": {
    "api-server": {
      "env": {
        "API_ENDPOINT": "https://api.example.com"
      }
    }
  }
}

2. Make Templates Environment-Aware

Use project.environment for environment-specific behavior:

json
{
  "mcpTemplates": {
    "smart-server": {
      "env": {
        "LOG_LEVEL": "{{#if (eq project.environment 'production')}}warn{{else}}debug{{/if}}",
        "CACHE_ENABLED": "{{#if (eq project.environment 'production')}}true{{else}}false{{/if}}"
      }
    }
  }
}

3. Validate with validateOnReload

Enable template validation during development:

json
{
  "templateSettings": {
    "validateOnReload": true,
    "failureMode": "strict"
  }
}

4. Use Graceful Failure in Production

Prevent template errors from breaking production:

json
{
  "templateSettings": {
    "validateOnReload": false,
    "failureMode": "graceful"
  }
}

5. Tag Templates Appropriately

Use static tags to enable proper filtering with presets:

json
{
  "mcpTemplates": {
    "team-server": {
      "tags": ["filesystem", "search", "team"]
    }
  }
}

Note: Template expressions inside tags arrays (e.g. "team-{{project.custom.team}}") are not rendered — they will be used as literal tag strings. Use static tag values only.

Template Instance Pool Policy

The Runtime Scope captures global Template Instance Pool policy from config.toml at startup:

toml
[templateSettings.pool]
maxInstancesPerTemplate = 50
maxTotalInstances = 100
idleTimeout = 300000
cleanupInterval = 30000

maxInstancesPerTemplate is the fallback for a Template Server definition that omits template.maxInstances; an explicit template value wins, including zero for unlimited per-template capacity. idleTimeout follows the same precedence, and zero makes an unreferenced instance immediately eligible for the next cleanup sweep. Every creation remains subject to the positive finite maxTotalInstances limit. One unreferenced cleanup timer removes idle membership, routing, transport, and pool state only after the effective timeout has elapsed.

Pool policy changes require an Aggregated Runtime restart. They do not resize the live pool or evict existing instances on generic configuration reload. The template-reprocessing circuit breaker remains fixed at three failures with a five-minute reset and is not part of this policy.

Troubleshooting

Template Not Rendering

Symptom: Template variables appear as literal {{variable}} strings

Solutions:

  1. Ensure templates are in mcpTemplates, not mcpServers
  2. Check that context is being collected (enable debug logging)
  3. Verify variable names match the context structure

Custom Context Missing

Symptom: {{project.custom.*}} variables are undefined

Solutions:

  1. Check .1mcprc file exists in project root
  2. Validate JSON syntax in .1mcprc
  3. Ensure context.custom object is properly structured

Server Not Starting

Symptom: Template server fails to start

Solutions:

  1. Check rendered configuration in logs
  2. Verify command path after template rendering
  3. Ensure environment variables are properly quoted

Conditional Logic Not Working

Symptom: {{#if}} conditions not evaluating as expected

Solutions:

  1. Use comparison helpers: {{#if (eq var 'value')}}
  2. Check for whitespace in values
  3. Enable debug logging to see actual values

See Also

Released under the Apache 2.0 License.