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:
| Feature | Static Servers (mcpServers) | Template Servers (mcpTemplates) |
|---|---|---|
| Configuration | Fixed values at startup | Dynamic values based on context |
| Context awareness | None | Project, user, transport, client |
| Multiple instances | Single instance per config | Multiple instances per context |
| Lifecycle | Always running | Created on-demand per connection |
| Use case | Stable infrastructure | Dynamic, 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:
{
"mcpTemplates": {
"project-context": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{project.path}}"],
"tags": ["filesystem", "project"]
}
}
}When a client connects, 1MCP:
- Collects context (project path, user, environment)
- Renders the template with actual values
- Creates a server instance with the rendered configuration
- Connects the client to the new instance
Environment-Specific Configuration
{
"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.*)
| Variable | Type | Description | Example |
|---|---|---|---|
project.path | string | Absolute path to current project | /Users/dev/myapp |
project.name | string | Project directory name | myapp |
project.environment | string | Environment name | development |
project.git.branch | string? | Git branch name | main |
project.git.commit | string? | Git commit hash | a1b2c3d |
project.git.repository | string? | Git remote URL | https://github.com/user/repo |
project.custom.* | any | Custom values from .1mcprc | User-defined |
User Variables (user.*)
| Variable | Type | Description | Example |
|---|---|---|---|
user.username | string? | System username | developer |
user.name | string? | User's full name | Jane Developer |
user.email | string? | User email | [email protected] |
user.home | string? | Home directory path | /Users/developer |
user.uid | string? | User ID | 501 |
user.gid | string? | Group ID | 20 |
user.shell | string? | Default shell | /bin/zsh |
Transport Variables (transport.*)
| Variable | Type | Description | Example |
|---|---|---|---|
transport.type | string | Transport protocol | http, sse, stdio |
transport.url | string? | Server URL (HTTP/SSE) | http://localhost:3050 |
transport.connectionId | string? | Connection identifier | conn_xyz789 |
transport.connectionTimestamp | string? | Connection time | 2025-01-25T10:30:00Z |
transport.client.name | string | Client application name | cursor, claude-code |
transport.client.version | string | Client version | 1.0.0 |
transport.client.title | string? | Client display name | Cursor Editor |
Template Syntax
1MCP uses Handlebars for template rendering. Variables use double curly braces: {{variable}}.
Variable Access
{{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:
{{#if (eq project.environment 'production')}}
<!-- Production configuration -->
{{else}}
<!-- Development configuration -->
{{/if}}Comparisons
Use built-in helpers for comparisons:
{{#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:
{{#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
{{#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:
{
"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
| Field | Type | Description |
|---|---|---|
preset | string | Default preset to use |
tags | string|string[] | Default tags for filtering |
context.projectId | string | Project identifier |
context.environment | string | Environment name (development, staging, production) |
context.team | string | Team name |
context.custom | object | Custom key-value pairs |
context.envPrefixes | string[] | Environment variable prefixes to include |
context.includeGit | boolean | Include Git information |
context.sanitizePaths | boolean | Sanitize file paths for security |
Accessing Custom Context
Custom values are available as {{project.custom.*}}:
{
"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:
{
"$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:
{
"templateSettings": {
"validateOnReload": true,
"failureMode": "graceful",
"cacheContext": true
}
}| Setting | Type | Default | Description |
|---|---|---|---|
validateOnReload | boolean | false | Reserved for future use; currently has no effect |
failureMode | 'strict'|'graceful' | 'strict' | How to handle template errors |
cacheContext | boolean | true | Cache 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:
{
"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
{{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
{{eq a b}} <!-- Equal -->
{{ne a b}} <!-- Not equal -->
{{gt a b}} <!-- Greater than -->
{{lt a b}} <!-- Less than -->Logical Helpers
{{and a b c}} <!-- All truthy -->
{{or a b c}} <!-- Any truthy -->String Helpers
{{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
{{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):
{
"context": {
"projectId": "myapp-api",
"team": "platform",
"custom": {
"apiEndpoint": "https://api.example.com"
}
}
}Avoid (hardcoded):
{
"mcpTemplates": {
"api-server": {
"env": {
"API_ENDPOINT": "https://api.example.com"
}
}
}
}2. Make Templates Environment-Aware
Use project.environment for environment-specific behavior:
{
"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:
{
"templateSettings": {
"validateOnReload": true,
"failureMode": "strict"
}
}4. Use Graceful Failure in Production
Prevent template errors from breaking production:
{
"templateSettings": {
"validateOnReload": false,
"failureMode": "graceful"
}
}5. Tag Templates Appropriately
Use static tags to enable proper filtering with presets:
{
"mcpTemplates": {
"team-server": {
"tags": ["filesystem", "search", "team"]
}
}
}Note: Template expressions inside
tagsarrays (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:
[templateSettings.pool]
maxInstancesPerTemplate = 50
maxTotalInstances = 100
idleTimeout = 300000
cleanupInterval = 30000maxInstancesPerTemplate 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:
- Ensure templates are in
mcpTemplates, notmcpServers - Check that context is being collected (enable debug logging)
- Verify variable names match the context structure
Custom Context Missing
Symptom: {{project.custom.*}} variables are undefined
Solutions:
- Check
.1mcprcfile exists in project root - Validate JSON syntax in
.1mcprc - Ensure
context.customobject is properly structured
Server Not Starting
Symptom: Template server fails to start
Solutions:
- Check rendered configuration in logs
- Verify command path after template rendering
- Ensure environment variables are properly quoted
Conditional Logic Not Working
Symptom: {{#if}} conditions not evaluating as expected
Solutions:
- Use comparison helpers:
{{#if (eq var 'value')}} - Check for whitespace in values
- Enable debug logging to see actual values
See Also
- Template Syntax Reference - Complete syntax and helper reference
- Custom Instructions Template - Customize LLM instructions (different feature)
- Context Collection - How context is collected
- Server Filtering - Tag-based filtering
