Skip to content

Commit 1f469a6

Browse files
committed
WIP
1 parent d9e601a commit 1f469a6

File tree

8 files changed

+155
-280
lines changed

8 files changed

+155
-280
lines changed

CLAUDE.md

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,16 @@ cmd/mcp-front/ # Main application entry point
168168
### Documentation Standards
169169

170170
**Write precise, technical language:**
171+
171172
- ❌ "When Claude connects to MCP Front, it includes a bearer token in the Authorization header"
172173
- ✅ "An MCP client can connect to MCP Front with a bearer token"
173-
- ❌ "Users log in with their Google account"
174+
- ❌ "Users log in with their Google account"
174175
- ✅ "Claude redirects users to Google for authentication"
175176
- ❌ "Claude establishes SSE connection"
176177
- ✅ "Claude connects via SSE"
177178

178179
**Key clarifications:**
180+
179181
- **Claude.ai only supports OAuth** - Bearer tokens are for development/alternative clients only
180182
- **Avoid redundant implementation details** - "bearer token" implies Authorization header
181183
- **Use precise actors** - "MCP client" not "user" in technical contexts
@@ -184,11 +186,10 @@ cmd/mcp-front/ # Main application entry point
184186
### Refactoring Guidelines
185187

186188
When refactoring for better design:
189+
187190
1. **Identify the core issue** - Don't just patch symptoms
188191
2. **Use proper dependency injection** - Pass dependencies to constructors
189-
3. **Extract interfaces to break circular dependencies** - Put in `internal/interfaces`
190-
4. **Test the refactoring** - Ensure all tests still pass
191-
5. **Update documentation** - Keep CLAUDE.md current with best practices
192+
3. **Test the refactoring** - Ensure all tests still pass
192193

193194
### Security Boundaries
194195

@@ -200,30 +201,32 @@ When refactoring for better design:
200201
## Quick Reference Commands
201202

202203
```bash
203-
# Build
204-
go build -o mcp-front ./cmd/mcp-front
204+
# Build everything
205+
make build
205206

206-
# Test
207-
go test ./...
208-
go test ./integration -v
207+
# Format everything
208+
make format
209209

210-
# Lint
211-
staticcheck ./...
210+
# Lint everything
211+
make lint
212212

213-
# Run locally
213+
# Test mcp-front
214+
go test ./internal/... -v
215+
go test ./integration -v
216+
217+
# Run mcp-front locally
214218
./mcp-front -config config.json
215219

216-
# Documentation site (from root)
217-
make doc # Start dev server
218-
make format # Format docs with Prettier
219-
make build # Build static site
220+
# Start docs dev server
221+
make doc
220222
```
221223

222224
## Documentation Site Guidelines
223225

224226
### Design Philosophy
225227

226228
The documentation site follows terse, to-the-point prose style (like early Stripe or Stainless docs):
229+
227230
- No bullet lists or tables in content
228231
- Conversational yet technical tone
229232
- Developer-to-developer communication
@@ -239,6 +242,7 @@ The documentation site follows terse, to-the-point prose style (like early Strip
239242
### Technical Implementation
240243

241244
**Structure** (docs-site/):
245+
242246
```
243247
src/
244248
├── components/
@@ -252,6 +256,7 @@ src/
252256
```
253257

254258
**Key Features**:
259+
255260
- Starlight theme with custom components and CSS overrides
256261
- Proper light/dark mode with automatic logo switching
257262
- 12-second animation cycle for subtle mascot behavior
@@ -260,26 +265,29 @@ src/
260265
### Animation Details
261266

262267
The animated logo creates a face-like character:
268+
263269
- **Eyes**: Left/right translation (1px) with synchronized movement
264-
- **Nose**: Subtle rotation (-1deg/+1deg) following eye direction
270+
- **Nose**: Subtle rotation (-1deg/+1deg) following eye direction
265271
- **Blinking**: Vertical scale (scaleY 0.1) with step-like timing for natural effect
266272
- **Timing**: 12-second cycle for easter egg discovery, not attention-grabbing
267273

268274
### Color Management
269275

270276
**CSS Custom Properties**:
277+
271278
```css
272279
:root {
273-
--sl-color-accent: #FF6B6B; /* Light mode */
280+
--sl-color-accent: #ff6b6b; /* Light mode */
274281
}
275282

276-
[data-theme='dark'] {
277-
--sl-color-accent: #FF6B6B; /* Buttons */
278-
--sl-color-text-accent: #333333; /* Text on red backgrounds */
283+
[data-theme="dark"] {
284+
--sl-color-accent: #ff6b6b; /* Buttons */
285+
--sl-color-text-accent: #333333; /* Text on red backgrounds */
279286
}
280287
```
281288

282289
**Specific Overrides**:
290+
283291
- GitHub icon: White in dark mode
284292
- Sidebar selection: Readable contrast
285293
- Anchor links: Light gray in dark mode
@@ -289,26 +297,30 @@ The animated logo creates a face-like character:
289297
### Content Guidelines
290298

291299
**Configuration Examples**:
300+
292301
- Always use `{"$env": "VAR"}` syntax, never bash `$VAR`
293302
- Match actual Go implementation exactly
294303
- Use realistic service names (e.g., "linear" not "database")
295304
- Include all required fields (version, transportType, etc.)
296305

297306
**Writing Style**:
307+
298308
- Flowing prose, not lists
299-
- Explain the "why" not just "how"
309+
- Explain the "why" not just "how"
300310
- Assume developer audience
301311
- Keep it concise but complete
302312

303313
### Pull Request Guidelines
304314

305315
**PR Titles**: Use clear, descriptive titles focused on the change impact, not just restating commit messages. Examples:
316+
306317
- ❌ "feat: add message endpoint support for SSE MCP servers"
307318
- ✅ "Add SSE message endpoint support"
308-
- ❌ "fix: implement session-specific tool registration for stdio clients"
319+
- ❌ "fix: implement session-specific tool registration for stdio clients"
309320
- ✅ "Fix stdio session tool handler conflicts"
310321

311322
**PR Descriptions**: Write terse prose for humans, not documentation. Avoid bullet lists unless they add genuine value. Focus on the problem solved and solution approach:
323+
312324
- Explain what was broken and how it's fixed
313325
- Use conversational, developer-to-developer tone
314326
- Skip implementation details unless critical for review

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ format:
55
go fmt ./...
66
cd docs-site && npm run format
77

8+
lint:
9+
staticcheck ./...
10+
golangci-lint run ./...
11+
812
build:
913
go build -o mcp-front ./cmd/mcp-front
1014
cd docs-site && npm run build
1115

12-
.PHONY: doc format build
16+
.PHONY: doc format build lint

docs-site/src/content/docs/api-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Main endpoint for MCP protocol communication over Server-Sent Events.
4343
The request flow:
4444

4545
1. Claude connects via SSE
46-
2. MCP Front validates auth token
46+
2. MCP Front validates auth token
4747
3. MCP Front connects to MCP server
4848
4. Bidirectional message streaming
4949

docs-site/src/content/docs/examples/oauth-google.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ The `allowedDomains` field is your primary security control. Only email addresse
9999
How it works:
100100

101101
- john@company.com ✓ Allowed
102-
- jane@subsidiary.com ✓ Allowed
102+
- jane@subsidiary.com ✓ Allowed
103103
- hacker@gmail.com ✗ Rejected
104104

105105
This is perfect for companies using Google Workspace, as it automatically grants access to all employees while blocking external users.
@@ -139,7 +139,7 @@ OAuth 2.1 requires HTTPS in production:
139139

140140
Options for HTTPS:
141141

142-
- **Load Balancer**: Terminate SSL at the load balancer
142+
- **Load Balancer**: Terminate SSL at the load balancer
143143
- **Reverse Proxy**: Use nginx/caddy with Let's Encrypt
144144
- **Managed hosting**: Deploy to any platform with automatic HTTPS
145145

internal/client/client.go

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -134,83 +134,6 @@ func DefaultTransportCreator(conf *config.MCPClientConfig) (MCPClientInterface,
134134
return nil, errors.New("invalid client type: must have either command or url")
135135
}
136136

137-
// AddToMCPServer connects the client to an MCP server
138-
func (c *Client) AddToMCPServer(ctx context.Context, clientInfo mcp.Implementation, mcpServer *server.MCPServer) error {
139-
return c.AddToMCPServerWithTokenCheck(ctx, clientInfo, mcpServer, "", false, nil, "", "", nil)
140-
}
141-
142-
// AddToMCPServerWithTokenCheck connects the client to an MCP server with optional token checking
143-
func (c *Client) AddToMCPServerWithTokenCheck(
144-
ctx context.Context,
145-
clientInfo mcp.Implementation,
146-
mcpServer *server.MCPServer,
147-
userEmail string,
148-
requiresToken bool,
149-
tokenStore storage.UserTokenStore,
150-
serverName string,
151-
setupBaseURL string,
152-
tokenSetup *config.TokenSetupConfig,
153-
) error {
154-
return c.AddToMCPServerWithSession(ctx, clientInfo, mcpServer, userEmail, requiresToken, tokenStore, serverName, setupBaseURL, tokenSetup, nil)
155-
}
156-
157-
// AddToMCPServerWithSession connects the client to an MCP server with optional session-specific tools
158-
func (c *Client) AddToMCPServerWithSession(
159-
ctx context.Context,
160-
clientInfo mcp.Implementation,
161-
mcpServer *server.MCPServer,
162-
userEmail string,
163-
requiresToken bool,
164-
tokenStore storage.UserTokenStore,
165-
serverName string,
166-
setupBaseURL string,
167-
tokenSetup *config.TokenSetupConfig,
168-
session server.ClientSession,
169-
) error {
170-
if c.needManualStart {
171-
err := c.client.Start(ctx)
172-
if err != nil {
173-
return err
174-
}
175-
}
176-
initRequest := mcp.InitializeRequest{}
177-
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
178-
initRequest.Params.ClientInfo = clientInfo
179-
initRequest.Params.Capabilities = mcp.ClientCapabilities{
180-
Experimental: make(map[string]interface{}),
181-
Roots: nil,
182-
Sampling: nil,
183-
}
184-
_, err := c.client.Initialize(ctx, initRequest)
185-
if err != nil {
186-
return err
187-
}
188-
internal.Logf("<%s> Successfully initialized MCP client", c.name)
189-
190-
// Start capability discovery
191-
internal.LogInfoWithFields("client", "Starting MCP capability discovery", map[string]interface{}{
192-
"server": c.name,
193-
})
194-
195-
err = c.addToolsToServer(ctx, mcpServer, userEmail, requiresToken, tokenStore, serverName, setupBaseURL, tokenSetup, session)
196-
if err != nil {
197-
return err
198-
}
199-
_ = c.addPromptsToServer(ctx, mcpServer)
200-
_ = c.addResourcesToServer(ctx, mcpServer)
201-
_ = c.addResourceTemplatesToServer(ctx, mcpServer)
202-
203-
internal.LogInfoWithFields("client", "MCP capability discovery completed", map[string]interface{}{
204-
"server": c.name,
205-
"userTokenRequired": requiresToken,
206-
})
207-
208-
if c.needPing {
209-
go c.startPingTask(ctx)
210-
}
211-
return nil
212-
}
213-
214137
// startPingTask runs a goroutine that pings the MCP server every 30 seconds.
215138
// The goroutine lifecycle is tied to the provided context:
216139
// - For stdio clients: context is cancelled when the request ends, stopping pings

internal/client/session_manager.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010

1111
"github.com/dgellow/mcp-front/internal"
1212
"github.com/dgellow/mcp-front/internal/config"
13+
"github.com/dgellow/mcp-front/internal/storage"
1314
"github.com/mark3labs/mcp-go/mcp"
15+
"github.com/mark3labs/mcp-go/server"
1416
)
1517

1618
var (
@@ -217,6 +219,75 @@ func (s *StdioSession) GetClient() *Client {
217219
return s.client
218220
}
219221

222+
// DiscoverAndRegisterCapabilities discovers and registers capabilities from the stdio process
223+
func (s *StdioSession) DiscoverAndRegisterCapabilities(
224+
ctx context.Context,
225+
mcpServer *server.MCPServer,
226+
userEmail string,
227+
requiresToken bool,
228+
tokenStore storage.UserTokenStore,
229+
serverName string,
230+
setupBaseURL string,
231+
tokenSetup *config.TokenSetupConfig,
232+
session server.ClientSession,
233+
) error {
234+
// Initialize the client
235+
if s.client.needManualStart {
236+
if err := s.client.client.Start(ctx); err != nil {
237+
return err
238+
}
239+
}
240+
241+
initRequest := mcp.InitializeRequest{}
242+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
243+
initRequest.Params.ClientInfo = mcp.Implementation{
244+
Name: serverName,
245+
Version: "1.0",
246+
}
247+
initRequest.Params.Capabilities = mcp.ClientCapabilities{
248+
Experimental: make(map[string]interface{}),
249+
Roots: nil,
250+
Sampling: nil,
251+
}
252+
253+
_, err := s.client.client.Initialize(ctx, initRequest)
254+
if err != nil {
255+
return err
256+
}
257+
internal.Logf("<%s> Successfully initialized MCP client", serverName)
258+
259+
// Start capability discovery
260+
internal.LogInfoWithFields("client", "Starting MCP capability discovery", map[string]interface{}{
261+
"server": serverName,
262+
})
263+
264+
// Discover and register tools
265+
if err := s.client.addToolsToServer(ctx, mcpServer, userEmail, requiresToken, tokenStore, serverName, setupBaseURL, tokenSetup, session); err != nil {
266+
return err
267+
}
268+
269+
// Discover and register prompts
270+
_ = s.client.addPromptsToServer(ctx, mcpServer)
271+
272+
// Discover and register resources
273+
_ = s.client.addResourcesToServer(ctx, mcpServer)
274+
275+
// Discover and register resource templates
276+
_ = s.client.addResourceTemplatesToServer(ctx, mcpServer)
277+
278+
internal.LogInfoWithFields("client", "MCP capability discovery completed", map[string]interface{}{
279+
"server": serverName,
280+
"userTokenRequired": requiresToken,
281+
})
282+
283+
// Start ping task if needed
284+
if s.client.needPing {
285+
go s.client.startPingTask(ctx)
286+
}
287+
288+
return nil
289+
}
290+
220291
// checkUserLimits verifies user hasn't exceeded session limits
221292
func (sm *StdioSessionManager) checkUserLimits(userEmail string) error {
222293
if userEmail == "" {

0 commit comments

Comments
 (0)