Tag Matching¶
Smart service selection with ± operators
Overview¶
When multiple providers offer the same capability, tags determine which one gets selected. MCP Mesh uses a scoring system with preference operators.
Tag Operators¶
| Operator | Prefix | Meaning | Effect |
|---|---|---|---|
| Required | none | Must be present | Provider rejected if missing |
| Preferred | + | Bonus if present | Higher score if present |
| Excluded | - | Must NOT be present | Provider rejected if present |
Examples¶
# Required: must have "claude"
# Preferred: bonus for "opus"
# Excluded: never use "experimental"
tags = ["claude", "+opus", "-experimental"]
Scoring Algorithm¶
Point Values¶
| Tag Type | Points |
|---|---|
| Required (present) | 5 points |
| Preferred (present) | 10 bonus points |
| Preferred (missing) | 0 points |
| Excluded (present) | Provider eliminated |
Scoring Example¶
Available providers:
# Provider A: claude-haiku
tags = ["claude", "haiku", "fast"]
# Provider B: claude-sonnet
tags = ["claude", "sonnet", "balanced"]
# Provider C: claude-opus
tags = ["claude", "opus", "premium"]
# Provider D: claude-experimental
tags = ["claude", "experimental"]
Consumer request:
Scoring:
| Provider | Calculation | Score | Result |
|---|---|---|---|
| claude-haiku | claude(5) | 5 | Candidate |
| claude-sonnet | claude(5) | 5 | Candidate |
| claude-opus | claude(5) + opus(10) | 15 | SELECTED |
| claude-experimental | ELIMINATED | - | Has "experimental" |
Use Cases¶
Cost Optimization¶
# Prefer cheaper models, exclude premium
@mesh.tool(dependencies=[{
"capability": "llm",
"tags": ["claude", "+haiku", "-premium"]
}])
Quality Prioritization¶
# Prefer highest quality
@mesh.tool(dependencies=[{
"capability": "llm",
"tags": ["claude", "+opus", "+premium"]
}])
Production Safety¶
# Never use experimental or beta
@mesh.tool(dependencies=[{
"capability": "database",
"tags": ["postgres", "-experimental", "-beta", "-staging"]
}])
Multi-Preference¶
# Multiple preferences (additive scoring)
@mesh.tool(dependencies=[{
"capability": "storage",
"tags": [
"s3", # Required
"+fast", # Prefer fast
"+ssd", # Prefer SSD
"+replicated", # Prefer replicated
"-beta" # Exclude beta
]
}])
Tie Breaking¶
When multiple providers have the same score:
- Version - Higher version wins
- Registration time - Earlier wins
- Random - If still tied
Combining with Versions¶
Tags work with version constraints:
@mesh.tool(dependencies=[{
"capability": "api",
"tags": ["rest", "+v2", "-deprecated"],
"version": ">=2.0.0,<3.0.0"
}])
Both tags AND version must match.
Namespace Interaction¶
Tags operate within namespaces:
@mesh.tool(dependencies=[{
"capability": "database",
"tags": ["+primary"],
"namespace": "production" # Only search production
}])
Best Practices¶
1. Use Descriptive Tags¶
# ✅ Good - clear categories
tags = ["llm", "claude", "sonnet", "balanced", "production"]
# ❌ Bad - vague
tags = ["service", "v1"]
2. Required + Preferred¶
# ✅ Good - required base, preferred optimization
tags = ["claude", "+opus", "+fast"]
# ❌ Bad - all required (too strict)
tags = ["claude", "opus", "fast"] # May find nothing
3. Safety Exclusions¶
# ✅ Always exclude unsafe options in production
tags = ["database", "-experimental", "-beta", "-test"]
4. Document Tag Conventions¶
# Project tag conventions:
# - llm providers: claude, openai, local
# - quality tiers: haiku, sonnet, opus
# - environments: production, staging, development
# - stability: stable, beta, experimental
Debugging¶
Check Available Tags¶
curl http://localhost:8000/agents | jq '.agents[].capabilities | to_entries[] | {capability: .key, tags: .value.tags}'
Test Matching¶
See Also¶
- Architecture - System overview
- Dependency Injection - DI patterns
- Capabilities & Tags - Full reference