Skip to content

Commit 5030f6e

Browse files
CopilotalexecCopilot
authored
Change mcp_servers map to single mcp_server configuration object in rules only (#164)
* Initial plan * Convert mcp_servers map to single mcp_server string field Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Clarify MCPServer() comment about rules being ignored Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Change mcp_servers map to single mcp_server config object Convert from map of server configs to single MCPServerConfig object. Each task/rule now specifies one MCP server configuration with standard fields (command, args, env, type, url, headers) plus arbitrary custom fields via the Content map. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Remove empty MCP server detection checks Remove the empty server detection logic from MCPServers() method as requested. The agent will handle filtering empty configs. Updated tests to expect empty MCPServerConfig structs in the returned slice. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Remove MCPServer field from TaskFrontMatter Tasks no longer have mcp_server field. MCP servers are now only specified in rules. Result.MCPServers() now only returns configs from rules. Updated tests, examples, and documentation. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Merge main branch and resolve conflicts Fetched from main and merged to resolve conflicts. All conflicts resolved: - Removed old task_frontmatter.go and rule_frontmatter.go files - Applied changes to new pkg/codingcontext/markdown/frontmatter.go structure - Removed MCPServer field from TaskFrontMatter - Changed RuleFrontMatter.MCPServers to MCPServer (single config) - Updated Result.MCPServers() to return []mcp.MCPServerConfig from rules only - Updated all tests to match new structure - All tests pass, linter passes Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> * Update examples/agents/tasks/example-mcp-arbitrary-fields.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update examples/agents/tasks/example-mcp-arbitrary-fields.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Filter empty MCP configs and fix documentation - Filter out empty MCP server configs in MCPServers() method - Only return configs with Command or URL set - Update tests to expect empty configs to be filtered - Remove duplicate standard fields list from example doc - Add documentation that empty configs are filtered Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> Co-authored-by: Alex Collins <alexec@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent d65f48d commit 5030f6e

9 files changed

Lines changed: 125 additions & 258 deletions

File tree

docs/reference/file-formats.md

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -137,35 +137,6 @@ timeout: 10m
137137
- `1h` - 1 hour
138138
- `1h30m` - 1 hour 30 minutes
139139

140-
#### `mcp_servers` (optional, standard field)
141-
142-
**Type:** Map (from server name to server configuration)
143-
**Purpose:** Specifies the MCP (Model Context Protocol) servers that the task should use; stored in frontmatter output but does not filter rules
144-
145-
The `mcp_servers` field is a **standard frontmatter field** following the industry standard for MCP server definition. It does not act as a selector. The field is a map where keys are server names and values are server configurations.
146-
147-
**Example:**
148-
```yaml
149-
---
150-
mcp_servers:
151-
filesystem:
152-
type: stdio
153-
command: npx
154-
args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"]
155-
git:
156-
type: stdio
157-
command: npx
158-
args: ["-y", "@modelcontextprotocol/server-git"]
159-
database:
160-
type: http
161-
url: https://api.example.com/mcp
162-
headers:
163-
Authorization: Bearer token123
164-
---
165-
```
166-
167-
**Note:** The format follows the MCP specification for server identification. Each server configuration includes a `type` field (e.g., "stdio", "http", "sse") and other fields specific to that transport type.
168-
169140
#### `agent` (optional, standard field)
170141

171142
**Type:** String
@@ -625,25 +596,38 @@ agent: cursor
625596
- If task/CLI specifies `agent: cursor`, only rules with `agent: cursor` or no agent field are included
626597
- Rules without an agent field are considered generic and always included (unless other selectors exclude them)
627598

628-
#### `mcp_servers` (rule metadata)
599+
#### `mcp_server` (rule metadata)
629600

630-
Specifies MCP servers that need to be running for this rule. Does not filter rules. The field is a map where keys are server names and values are server configurations.
601+
Specifies an MCP server configuration for this rule. Each rule can specify one MCP server configuration with standard and arbitrary custom fields. Does not filter rules.
602+
603+
**Important:** MCP servers are specified in rules only, not in tasks. Tasks select rules (and thus MCP servers) via selectors.
631604

632605
```yaml
633606
---
634-
mcp_servers:
635-
filesystem:
636-
type: stdio
637-
command: npx
638-
args: ["-y", "@modelcontextprotocol/server-filesystem"]
639-
database:
640-
type: http
641-
url: https://api.example.com/mcp
607+
mcp_server:
608+
command: python
609+
args: ["-m", "server"]
610+
env:
611+
PYTHON_PATH: /usr/bin/python3
612+
custom_config:
613+
host: localhost
614+
port: 5432
642615
---
643-
# Metadata indicating required MCP servers
616+
# Rule with MCP server configuration
644617
```
645618

646-
**Note:** This field is informational and does not affect rule selection.
619+
**Standard configuration fields:**
620+
- `command`: The executable to run (e.g., "npx", "python", "docker")
621+
- `args`: Array of command-line arguments
622+
- `env`: Map of environment variables
623+
- `type`: Connection protocol - "stdio" (default), "http", or "sse"
624+
- `url`: Endpoint URL (required for HTTP/SSE types)
625+
- `headers`: Custom HTTP headers (for HTTP/SSE types)
626+
627+
**Additional arbitrary fields:**
628+
You can include any custom fields for your specific server needs (e.g., `custom_config`, `monitoring`, `cache_enabled`, etc.). All fields are preserved in the configuration.
629+
630+
**Note:** This field is metadata and does not affect rule selection.
647631

648632
#### `expand` (optional)
649633

Lines changed: 48 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,58 @@
11
---
22
task_name: example-mcp-arbitrary-fields
33
agent: cursor
4-
mcp_servers:
5-
# Example with standard fields only
6-
filesystem:
7-
type: stdio
8-
command: filesystem
9-
10-
# Example with standard fields plus arbitrary custom fields
11-
custom-database:
12-
type: stdio
13-
command: database-mcp
14-
args: ["--verbose"]
15-
# Arbitrary fields below
16-
cache_enabled: true
17-
max_cache_size: 1000
18-
connection_pool_size: 10
19-
20-
# Example HTTP server with custom metadata
21-
api-server:
22-
type: http
23-
url: https://api.example.com
24-
headers:
25-
Authorization: Bearer token123
26-
# Arbitrary fields below
27-
api_version: v2
28-
rate_limit: 100
29-
timeout_seconds: 30
30-
retry_policy: exponential
31-
region: us-west-2
32-
33-
# Example with nested custom configuration
34-
advanced-server:
35-
type: stdio
36-
command: python
37-
args: ["-m", "server"]
38-
env:
39-
PYTHON_PATH: /usr/bin/python3
40-
# Arbitrary nested fields below
41-
custom_config:
42-
host: localhost
43-
port: 5432
44-
ssl: true
45-
pool:
46-
min: 2
47-
max: 10
48-
monitoring:
49-
enabled: true
50-
metrics_port: 9090
514
---
525

53-
# Example Task with Arbitrary MCP Server Fields
6+
# Example Rule with MCP Server Configuration
7+
8+
This example demonstrates how rules can specify MCP server configuration with arbitrary custom fields.
9+
10+
Note: MCP servers are specified in rules, not in tasks. Tasks can select which rules (and thus which MCP servers) to use via selectors.
11+
12+
## The `mcp_server` Field in Rules
13+
14+
Rules can specify a single MCP server configuration with both standard and arbitrary custom fields.
15+
16+
The `mcp_server` field, when present in a rule, specifies that rule's single MCP server configuration with both standard and arbitrary custom fields. Tasks cannot define MCP servers directly.
17+
18+
**Standard fields:**
19+
- `command`: The executable to run (e.g., "python", "npx", "docker")
20+
- `args`: Array of command-line arguments
21+
- `env`: Environment variables for the server process
22+
- `type`: Connection protocol ("stdio", "http", "sse") - optional, defaults to stdio
23+
- `url`: Endpoint URL for HTTP/SSE types
24+
- `headers`: Custom HTTP headers for HTTP/SSE types
25+
26+
## Example Rule with MCP Server
27+
28+
```yaml
29+
---
30+
rule_name: python-mcp-server
31+
mcp_server:
32+
command: python
33+
args: ["-m", "server"]
34+
env:
35+
PYTHON_PATH: /usr/bin/python3
36+
custom_config:
37+
host: localhost
38+
port: 5432
39+
ssl: true
40+
pool:
41+
min: 2
42+
max: 10
43+
monitoring:
44+
enabled: true
45+
metrics_port: 9090
46+
---
47+
48+
# Python MCP Server Rule
5449

55-
This task demonstrates the ability to add arbitrary fields to MCP server configurations, just like we can with FrontMatter.
50+
This rule provides the Python MCP server configuration.
51+
```
5652

5753
## Why Arbitrary Fields?
5854

59-
Different MCP servers may need different configuration options beyond the standard fields (`type`, `command`, `args`, `env`, `url`, `headers`). Arbitrary fields allow you to:
55+
Different MCP servers may need different configuration options beyond the standard fields. Arbitrary fields allow you to:
6056

6157
1. **Add custom metadata**: Version info, regions, endpoints, etc.
6258
2. **Configure behavior**: Caching, retry policies, timeouts, rate limits
@@ -65,7 +61,7 @@ Different MCP servers may need different configuration options beyond the standa
6561

6662
## How It Works
6763

68-
The `MCPServerConfig` struct now includes a `Content` field (similar to `BaseFrontMatter`) that captures all fields from YAML/JSON:
64+
The `MCPServerConfig` struct includes a `Content` field that captures all fields from YAML/JSON:
6965

7066
```go
7167
type MCPServerConfig struct {
@@ -82,12 +78,4 @@ type MCPServerConfig struct {
8278
}
8379
```
8480

85-
## Example Usage
86-
87-
The examples above show:
88-
- **Simple custom fields**: `cache_enabled`, `max_cache_size`
89-
- **API configuration**: `api_version`, `rate_limit`, `timeout_seconds`
90-
- **Nested objects**: `custom_config` with sub-fields like `host`, `port`, `ssl`
91-
- **Multiple custom sections**: `custom_config` and `monitoring` as separate objects
92-
93-
All these fields are preserved when the configuration is parsed and can be accessed via the `Content` map.
81+
All fields (both standard and custom) are preserved when the configuration is parsed and can be accessed via the struct fields or the `Content` map.

examples/agents/tasks/example-with-standard-fields.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ language: go
55
model: anthropic.claude-sonnet-4-20250514-v1-0
66
single_shot: false
77
timeout: 10m
8-
mcp_servers:
9-
filesystem:
10-
type: stdio
11-
command: filesystem
12-
git:
13-
type: stdio
14-
command: git
158
selectors:
169
stage: implementation
1710
---
@@ -34,7 +27,6 @@ These fields are stored in frontmatter and passed through to output, but do NOT
3427
- **model**: `anthropic.claude-sonnet-4-20250514-v1-0` - AI model to use for this task
3528
- **single_shot**: `false` - Task can be run multiple times
3629
- **timeout**: `10m` - Task timeout as time.Duration (10 minutes)
37-
- **mcp_servers**: `[filesystem, git]` - MCP servers required for this task
3830

3931
## Custom Selectors
4032

pkg/codingcontext/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ func main() {
116116

117117
// Access MCP server configurations
118118
mcpServers := result.MCPServers()
119-
for name, config := range mcpServers {
120-
fmt.Printf("MCP Server %s: %s\n", name, config.Command)
119+
for i, config := range mcpServers {
120+
fmt.Printf("MCP Server %d: %s\n", i, config.Command)
121121
}
122122
}
123123
```
@@ -139,7 +139,7 @@ Result holds the assembled context from running a task:
139139
- `Agent Agent` - The agent used (from task frontmatter or option)
140140

141141
**Methods:**
142-
- `MCPServers() MCPServerConfigs` - Returns all MCP server configurations from rules and task
142+
- `MCPServers() []MCPServerConfig` - Returns all MCP server configurations from rules as a slice
143143

144144
#### `Markdown[T]`
145145

pkg/codingcontext/markdown/frontmatter.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ type TaskFrontMatter struct {
3535
// Does not filter rules, metadata only
3636
Timeout string `yaml:"timeout,omitempty" json:"timeout,omitempty"`
3737

38-
// MCPServers maps server names to their configurations
39-
// Does not filter rules, metadata only
40-
MCPServers mcp.MCPServerConfigs `yaml:"mcp_servers,omitempty" json:"mcp_servers,omitempty"`
41-
4238
// Resume indicates if this task should be resumed
4339
Resume bool `yaml:"resume,omitempty" json:"resume,omitempty"`
4440

@@ -120,9 +116,9 @@ type RuleFrontMatter struct {
120116
// Agent specifies which AI agent this rule is intended for
121117
Agent string `yaml:"agent,omitempty" json:"agent,omitempty"`
122118

123-
// MCPServers maps server names to their configurations
119+
// MCPServer specifies a single MCP server configuration
124120
// Metadata only, does not filter
125-
MCPServers mcp.MCPServerConfigs `yaml:"mcp_servers,omitempty" json:"mcp_servers,omitempty"`
121+
MCPServer mcp.MCPServerConfig `yaml:"mcp_server,omitempty" json:"mcp_server,omitempty"`
126122

127123
// RuleName is an optional identifier for the rule file
128124
RuleName string `yaml:"rule_name,omitempty" json:"rule_name,omitempty"`

pkg/codingcontext/markdown/frontmatter_rule_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@ agent: cursor
5151
TaskNames: []string{"test-task"},
5252
Languages: []string{"go", "python"},
5353
Agent: "copilot",
54-
MCPServers: mcp.MCPServerConfigs{
55-
"database": {
56-
Type: mcp.TransportTypeStdio,
57-
Command: "database-server",
58-
},
54+
MCPServer: mcp.MCPServerConfig{
55+
Type: mcp.TransportTypeStdio,
56+
Command: "database-server",
57+
Args: []string{"--port", "5432"},
5958
},
6059
RuleName: "test-rule",
6160
},
@@ -65,10 +64,12 @@ languages:
6564
- go
6665
- python
6766
agent: copilot
68-
mcp_servers:
69-
database:
70-
type: stdio
71-
command: database-server
67+
mcp_server:
68+
type: stdio
69+
command: database-server
70+
args:
71+
- --port
72+
- "5432"
7273
rule_name: test-rule
7374
`,
7475
},

pkg/codingcontext/markdown/frontmatter_task_test.go

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"testing"
55

66
"github.com/goccy/go-yaml"
7-
"github.com/kitproj/coding-context-cli/pkg/codingcontext/mcp"
87
)
98

109
func TestTaskFrontMatter_Marshal(t *testing.T) {
@@ -33,11 +32,7 @@ func TestTaskFrontMatter_Marshal(t *testing.T) {
3332
Model: "gpt-4",
3433
SingleShot: true,
3534
Timeout: "10m",
36-
MCPServers: mcp.MCPServerConfigs{
37-
"filesystem": {Type: mcp.TransportTypeStdio, Command: "filesystem-server"},
38-
"git": {Type: mcp.TransportTypeStdio, Command: "git-server"},
39-
},
40-
Resume: false,
35+
Resume: false,
4136
Selectors: map[string]any{
4237
"stage": "implementation",
4338
},
@@ -49,13 +44,6 @@ languages:
4944
model: gpt-4
5045
single_shot: true
5146
timeout: 10m
52-
mcp_servers:
53-
filesystem:
54-
type: stdio
55-
command: filesystem-server
56-
git:
57-
type: stdio
58-
command: git-server
5947
selectors:
6048
stage: implementation
6149
`,
@@ -142,13 +130,6 @@ languages:
142130
model: gpt-4
143131
single_shot: true
144132
timeout: 10m
145-
mcp_servers:
146-
filesystem:
147-
type: stdio
148-
command: filesystem-server
149-
git:
150-
type: stdio
151-
command: git-server
152133
selectors:
153134
stage: implementation
154135
`,
@@ -161,10 +142,6 @@ selectors:
161142
Model: "gpt-4",
162143
SingleShot: true,
163144
Timeout: "10m",
164-
MCPServers: mcp.MCPServerConfigs{
165-
"filesystem": {Type: mcp.TransportTypeStdio, Command: "filesystem-server"},
166-
"git": {Type: mcp.TransportTypeStdio, Command: "git-server"},
167-
},
168145
Selectors: map[string]any{
169146
"stage": "implementation",
170147
},

0 commit comments

Comments
 (0)