Skip to content

Commit 15b5bab

Browse files
dgellowclaude
andauthored
feat: add inline MCP servers (#5)
* feat: add inline MCP server support Implement inline MCP servers that can be defined directly in config without needing separate MCP server processes. This allows quick creation of simple tools using any command (docker, shell, etc). Key features: - Define tools directly in mcp-front config - Execute any command with template argument support - Support for environment variables using {"$env": "..."} syntax - Full SSE/JSON-RPC protocol implementation - Flexible execution model (docker, firecracker, raw commands) Example use cases: - Wrapping CLI tools (gcloud, aws, kubectl) - Simple data transformations - Quick debugging utilities 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * test: add comprehensive tests for inline MCP servers - Add interface-based dependency injection for better testability - Implement proper JSON-RPC over HTTP status codes (400 for invalid JSON) - Create thorough unit tests for handler, server, and resolver components - Test SSE functionality without artificial timeouts or infinite loops - Follow Go idioms and existing project test patterns - Fix ParseConfigValue capitalization in config tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: update OAuth tests to use correct binary and improve inline MCP servers - Fix OAuth tests to use startMCPFront helper instead of hardcoded binary path - Update remaining OAuth test functions to use correct binary location - Remove unhelpful comment from test configuration - Improve inline MCP server session management with secure session IDs - Add proper message endpoint path for MCP protocol compliance - Update test utilities to support both stdio and inline server types - Add comprehensive integration tests for inline MCP servers 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: add proper error handling for JSON encoding in inline handlers - Check error returns from json.NewEncoder().Encode() calls - Add error logging for failed JSON operations - Fix golangci-lint errcheck violations in inline MCP server code 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: remove bash syntax from config files and use native tool env vars - Replace complex shell commands with simple gcloud/gsutil commands - Use native GOOGLE_APPLICATION_CREDENTIALS and CLOUDSDK_CORE_PROJECT env vars - Remove all bash-style $VAR syntax and temp file creation - Simplify test config to use direct echo command - All configs now pass validation without warnings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * docs: improve documentation and apply go fmt - Add communication and design philosophy section to CLAUDE.md - Update README with explanation of JSON env var syntax design rationale - Apply go fmt to ensure consistent code formatting across the project 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: remove template substitution from inline MCP servers - Remove processTemplateArgs function and template processing - Update tests to not expect template substitution - Fix config examples to use static arguments - Keep Docker -e flags for environment variable passing - Inline servers now work exactly like stdio but per-tool Template substitution was an unnecessary feature that: - Added complexity - Was inconsistent with stdio servers - Violated mcp-front's explicit configuration philosophy - Was incorrectly placed in the inline package 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * refactor: create centralized JSON-RPC package - Create internal/jsonrpc package with types, errors, and writer utilities - Migrate inline handler to use the new package - Migrate server MCP handler to use the new package - Replace all interface{} with any - Fix JSON-RPC error HTTP status (should be 200 for valid errors) - Remove duplicate JSON-RPC error handling code This centralizes all JSON-RPC handling in one place, making it easier to maintain and ensuring consistent behavior across the codebase. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: implement systematic t.Cleanup() for integration tests - Add automatic cleanup registration in startMCPFront() - Remove manual defer stopMCPFront() calls from all tests - Implement graceful shutdown (SIGINT → SIGKILL after 5s timeout) - Eliminate orphaned process issues that caused port conflicts - Simplify test code by removing unused command variables - Complete code deduplication with centralized json/cookie packages - Update CLAUDE.md communication standards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 38eeb30 commit 15b5bab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2660
-377
lines changed

CLAUDE.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,34 @@ staticcheck ./...
198198
./mcp-front -config config.json
199199
```
200200

201-
Remember: Think like an experienced engineer - understand the use cases, read the docs, plan properly, then execute.
201+
## Communication & Design Philosophy
202+
203+
### Understanding Sam's Standards
204+
205+
1. **Zero tolerance for workarounds** - If something is wrong, fix it properly. Don't try to work around design decisions.
206+
207+
2. **Config syntax is sacred** - The `{"$env": "VAR"}` syntax exists to prevent shell injection risks. NO bash syntax (`$VAR`) anywhere in configs, ever. This isn't arbitrary - it prevents real security issues when configs pass through multiple layers of tooling.
208+
209+
3. **Simplicity over cleverness** - When you see complex shell commands or temp file creation, the answer is usually "use the tool's native features instead." Don't preserve unnecessary complexity.
210+
211+
4. **Read the code first** - Don't guess how systems work. Read `ParseConfigValue` and similar functions to understand the actual implementation.
212+
213+
5. **High signal communication** - Expect direct feedback. "That's really bad" or "good fucking damn" are proportional to how badly you're missing the point. This isn't personal - it's clear communication about what matters.
214+
215+
### Common Misunderstandings to Avoid
216+
217+
1. **Don't confuse config parsing with runtime templating** - `{"$env": "VAR"}` is resolved during config load, not at runtime.
218+
219+
2. **Don't assume complexity where none exists** - This codebase favors simple, explicit solutions.
220+
221+
3. **Don't bring patterns from other systems** - Understand THIS system's design choices.
222+
223+
### When You're Stuck
224+
225+
- **ASK QUESTIONS** - Even "dumb" questions are better than wrong assumptions
226+
- If tests are failing, fix the code, not the tests
227+
- If you're working around a design choice, stop and ask why it exists
228+
- Never hide problems - surface issues immediately
229+
- **Do NOT simulate negative human emotions** - No "panicking", "freaking out", "frustration", etc.
230+
231+
Remember: Think like an experienced engineer - understand the use cases, read the docs, plan properly, then execute. But most importantly, when uncertain, ASK rather than guess.

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,7 @@ docker run -d -p 8080:8080 \
139139

140140
### Environment variable formats
141141

142-
- **Environment variables**: Use `{"$env": "VAR_NAME"}` everywhere
143-
- **User tokens**: Use `{"$userToken": "{{token}}"}` for per-user authentication
142+
mcp-front uses explicit JSON syntax `{"$env": "VAR_NAME"}` for environment variables throughout its configuration. This deliberate choice eliminates the ambiguity and risks inherent in shell-style variable substitution. When configs pass through multiple layers of tooling and scripts, traditional `$VAR` syntax can expand unexpectedly, causing security issues and debugging nightmares. The JSON format ensures your configuration remains exactly as written until mcp-front processes it, providing predictable behavior across all deployment environments. For per-user authentication, `{"$userToken": "{{token}}"}` follows the same principle, keeping user credentials cleanly separated from system configuration.
144143

145144
### Per-user tokens
146145

@@ -157,7 +156,7 @@ Some MCP servers are better to use with each users having their own integration
157156
"helpUrl": "https://www.notion.so/my-integrations"
158157
},
159158
"command": "docker",
160-
"args": ["run", "--rm", "-i", "mcp/notion:latest"],
159+
"args": ["run", "--rm", "-i", "-e", "OPENAPI_MCP_HEADERS", "mcp/notion:latest"],
161160
"env": {
162161
"OPENAPI_MCP_HEADERS": {
163162
"$userToken": "{\"Authorization\": \"Bearer {{token}}\"}"

config-inline-example.json

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
{
2+
"version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES",
3+
"proxy": {
4+
"baseURL": "https://mcp.example.com",
5+
"addr": ":8080",
6+
"name": "MCP Front with Inline Servers",
7+
"auth": {
8+
"kind": "oauth",
9+
"issuer": "https://mcp.example.com",
10+
"allowedDomains": ["example.com"],
11+
"allowedOrigins": ["https://claude.ai"],
12+
"tokenTtl": "1h",
13+
"storage": "memory",
14+
"googleClientId": {"$env": "GOOGLE_CLIENT_ID"},
15+
"googleClientSecret": {"$env": "GOOGLE_CLIENT_SECRET"},
16+
"googleRedirectUri": "https://mcp.example.com/oauth/callback",
17+
"jwtSecret": {"$env": "JWT_SECRET"},
18+
"encryptionKey": {"$env": "ENCRYPTION_KEY"}
19+
}
20+
},
21+
"mcpServers": {
22+
"gcloud": {
23+
"transportType": "inline",
24+
"inline": {
25+
"description": "Google Cloud Platform debugging tools",
26+
"tools": [
27+
{
28+
"name": "list_instances",
29+
"description": "List all GCE instances in the configured project",
30+
"inputSchema": {
31+
"type": "object",
32+
"properties": {},
33+
"required": []
34+
},
35+
"command": "docker",
36+
"args": [
37+
"run",
38+
"--rm",
39+
"-i",
40+
"-e",
41+
"GOOGLE_APPLICATION_CREDENTIALS",
42+
"-e",
43+
"CLOUDSDK_CORE_PROJECT",
44+
"google/cloud-sdk:alpine",
45+
"gcloud",
46+
"compute",
47+
"instances",
48+
"list",
49+
"--format=json"
50+
],
51+
"env": {
52+
"GOOGLE_APPLICATION_CREDENTIALS": {"$env": "GCP_SERVICE_ACCOUNT_JSON"},
53+
"CLOUDSDK_CORE_PROJECT": {"$env": "GCP_PROJECT_ID"}
54+
},
55+
"timeout": "30s"
56+
},
57+
{
58+
"name": "describe_cluster",
59+
"description": "Get details about the default GKE cluster (hardcoded to my-cluster in us-central1-a)",
60+
"inputSchema": {
61+
"type": "object",
62+
"properties": {},
63+
"required": []
64+
},
65+
"command": "docker",
66+
"args": [
67+
"run",
68+
"--rm",
69+
"-i",
70+
"-e",
71+
"GOOGLE_APPLICATION_CREDENTIALS",
72+
"-e",
73+
"CLOUDSDK_CORE_PROJECT",
74+
"google/cloud-sdk:alpine",
75+
"gcloud",
76+
"container",
77+
"clusters",
78+
"describe",
79+
"my-cluster",
80+
"--zone=us-central1-a",
81+
"--format=json"
82+
],
83+
"env": {
84+
"GOOGLE_APPLICATION_CREDENTIALS": {"$env": "GCP_SERVICE_ACCOUNT_JSON"},
85+
"CLOUDSDK_CORE_PROJECT": {"$env": "GCP_PROJECT_ID"}
86+
},
87+
"timeout": "30s"
88+
},
89+
{
90+
"name": "list_buckets",
91+
"description": "List all Cloud Storage buckets in the configured project",
92+
"inputSchema": {
93+
"type": "object",
94+
"properties": {},
95+
"required": []
96+
},
97+
"command": "docker",
98+
"args": [
99+
"run",
100+
"--rm",
101+
"-i",
102+
"-e",
103+
"GOOGLE_APPLICATION_CREDENTIALS",
104+
"-e",
105+
"CLOUDSDK_CORE_PROJECT",
106+
"google/cloud-sdk:alpine",
107+
"gsutil",
108+
"ls"
109+
],
110+
"env": {
111+
"GOOGLE_APPLICATION_CREDENTIALS": {"$env": "GCP_SERVICE_ACCOUNT_JSON"},
112+
"CLOUDSDK_CORE_PROJECT": {"$env": "GCP_PROJECT_ID"}
113+
},
114+
"timeout": "30s"
115+
}
116+
]
117+
}
118+
}
119+
}
120+
}

config-inline-test.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES",
3+
"proxy": {
4+
"baseURL": "http://localhost:8080",
5+
"addr": ":8080",
6+
"name": "MCP Front with Inline Test",
7+
"auth": {
8+
"kind": "oauth",
9+
"issuer": "http://localhost:8080",
10+
"allowedDomains": ["gmail.com"],
11+
"allowedOrigins": ["https://claude.ai"],
12+
"tokenTtl": "1h",
13+
"storage": "memory",
14+
"googleClientId": {"$env": "GOOGLE_CLIENT_ID"},
15+
"googleClientSecret": {"$env": "GOOGLE_CLIENT_SECRET"},
16+
"googleRedirectUri": "http://localhost:8080/oauth/callback",
17+
"jwtSecret": {"$env": "JWT_SECRET"},
18+
"encryptionKey": {"$env": "ENCRYPTION_KEY"}
19+
}
20+
},
21+
"mcpServers": {
22+
"system": {
23+
"transportType": "inline",
24+
"inline": {
25+
"description": "System information tools",
26+
"tools": [
27+
{
28+
"name": "echo",
29+
"description": "Echo a message back",
30+
"inputSchema": {
31+
"type": "object",
32+
"properties": {
33+
"message": {
34+
"type": "string",
35+
"description": "Message to echo"
36+
}
37+
},
38+
"required": ["message"]
39+
},
40+
"command": "echo",
41+
"args": [{"$env": "TEST_ENV_VAR"}]
42+
},
43+
{
44+
"name": "date",
45+
"description": "Get current date and time",
46+
"inputSchema": {
47+
"type": "object",
48+
"properties": {}
49+
},
50+
"command": "date"
51+
},
52+
{
53+
"name": "env_test",
54+
"description": "Test environment variable resolution",
55+
"inputSchema": {
56+
"type": "object",
57+
"properties": {}
58+
},
59+
"command": "echo",
60+
"args": [{"$env": "TEST_ENV_VAR"}]
61+
}
62+
]
63+
}
64+
}
65+
}
66+
}

config-oauth-firestore.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"notion": {
3636
"transportType": "stdio",
3737
"command": "docker",
38-
"args": ["run", "--rm", "-i", "mcp/notion"],
38+
"args": ["run", "--rm", "-i", "-e", "NOTION_TOKEN", "mcp/notion"],
3939
"env": {
4040
"NOTION_TOKEN": {"$env": "NOTION_TOKEN"}
4141
}

config-oauth.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"notion": {
3636
"transportType": "stdio",
3737
"command": "docker",
38-
"args": ["run", "--rm", "-i", "mcp/notion"],
38+
"args": ["run", "--rm", "-i", "-e", "NOTION_TOKEN", "mcp/notion"],
3939
"env": {
4040
"NOTION_TOKEN": {"$env": "NOTION_TOKEN"}
4141
}

config-oauth.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"command": "docker",
4141
"args": [
4242
"run", "--rm", "-i",
43+
"-e", "OPENAPI_MCP_HEADERS",
4344
"mcp/notion:latest"
4445
],
4546
"env": {

config-token.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"notion": {
2727
"transportType": "stdio",
2828
"command": "docker",
29-
"args": ["run", "--rm", "-i", "mcp/notion"],
29+
"args": ["run", "--rm", "-i", "-e", "NOTION_TOKEN", "mcp/notion"],
3030
"env": {
3131
"NOTION_TOKEN": "test-notion-token"
3232
}

config-user-tokens-example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"notion-user": {
2424
"transportType": "stdio",
2525
"command": "docker",
26-
"args": ["run", "--rm", "-i", "mcp/notion"],
26+
"args": ["run", "--rm", "-i", "-e", "OPENAPI_MCP_HEADERS", "mcp/notion"],
2727
"env": {
2828
"OPENAPI_MCP_HEADERS": {"$userToken": "{\"Authorization\": \"Bearer {{token}}\"}"}
2929
},

0 commit comments

Comments
 (0)