Commit 31e555b
feat: add gram install command for MCP server configuration & support common clients (#776)
## Summary
Adds new `gram install` command with support for configuring Gram
toolsets as MCP servers across multiple AI clients. The implementation
uses a shared resolver architecture with client-specific configuration
writers.
## Supported Clients
- **Claude Code**: Native HTTP transport with `.mcp.json` configuration
- **Claude Desktop**: `.dxt` file generation for one-click installation
- **Cursor**: Browser-based installation flow (gracefully falls back to
manual URL if browser fails)
- **Gemini CLI**: Configuration file generation
## Key Features
### Automatic Configuration
```
gram install claude-code --toolset speakeasy-admin
```
- Fetches toolset metadata from Gram API
- Automatically derives MCP URL from organization/project/environment or
custom MCP slug
- Extracts environment variable names from toolset security config
- Uses standard `Authorization` header for HTTP authentication
- Uses toolset name as the MCP server name
### Manual Configuration
```
gram install claude-code \
--mcp-url https://mcp.getgram.ai/org/project/environment \
--api-key your-api-key \
--header Custom-Auth-Header \
--env-var MY_API_KEY
```
- Supports custom MCP URLs for any MCP server
- Configurable authentication headers (defaults to `Authorization`)
- Environment variable substitution for secure API key storage
- Automatic detection of locally set environment variables (uses actual
value if available)
### Shared Architecture
All install commands share:
- Common toolset resolver (\`cli/internal/mcp/resolver.go\`) for
fetching and deriving configuration
- Consistent flag interface across all clients
- Client-specific writers for different configuration formats:
- \`.mcp.json\` for Claude Code (HTTP transport)
- \`.dxt\` files for Claude Desktop
- Browser URLs for Cursor and Gemini CLI
- \`.claude\` config for Claude CLI
## Implementation Highlights
### Native HTTP Transport
Switched from \`mcp-remote\` npm package to native HTTP transport for
Claude Code:
- Eliminates external npm dependency
- Better control over transport configuration
- Direct HTTP communication with MCP servers
- Simpler configuration format
### Smart Environment Variable Handling
\`\`\`bash
# If MY_KEY is already set locally, uses the actual value
export MY_KEY=secret123
gram install claude-code --toolset my-toolset --env-var MY_KEY
# If MY_KEY is not set, uses substitution syntax \${MY_KEY}
unset MY_KEY
gram install claude-code --toolset my-toolset --env-var MY_KEY
\`\`\`
This allows flexibility for both:
1. Development: Use actual values when env vars are set
2. Distribution: Use substitution when sharing configs
### Cross-Platform Compatibility
- **Linux Support**: Downloads directory detection with fallback to
current directory
- **Graceful Degradation**: Browser opening failures don't block Cursor
installation (shows manual URL)
- **Standard Headers**: Uses `Authorization` header following HTTP
conventions
## Command-Line Interface
### Common Flags
- \`--toolset\` - Automatic configuration via Gram API lookup
- \`--mcp-url\` - Manual MCP server URL
- \`--name\` - Custom server name (optional)
- \`--header\` - HTTP header name (defaults to `Authorization`)
- \`--env-var\` - Environment variable for API key substitution
### Client-Specific Flags
- \`--output-dir\` (Claude Desktop) - Where to save .dxt file
## Files Changed
### CLI Implementation
- \`cli/internal/app/install.go\` - Main install command with shared
resolver
- \`cli/internal/app/install_claude_code.go\` - Claude Code specific
installer
- \`cli/internal/app/install_claude_desktop.go\` - Claude Desktop
specific installer
- \`cli/internal/app/install_cursor.go\` - Cursor specific installer
- \`cli/internal/app/install_gemini_cli.go\` - Gemini CLI specific
installer
### MCP Configuration
- \`cli/internal/mcp/resolver.go\` - Shared toolset resolution logic
- \`cli/internal/mcp/config.go\` - Common MCP configuration types
- \`cli/internal/mcp/browser.go\` - Browser-based installation helper
- \`cli/internal/mcp/claude_cli.go\` - Claude CLI config writer
- \`cli/internal/mcp/dxt.go\` - DXT file generation for Claude Desktop
- \`cli/internal/mcp/gemini_cli.go\` - Gemini CLI config writer
### Legacy (Claude Code specific)
- \`cli/internal/claudecode/config.go\` - Claude Code \`.mcp.json\`
utilities
### API Clients
- \`cli/internal/api/toolsets.go\` - Toolset API client
- \`cli/internal/api/assets.go\` - Added missing ServeFunction endpoint
### Server Changes
- \`server/design/toolsets/design.go\` - Added support for API key auth
on toolset endpoints
- Various server files - API key authentication support for toolset
serving
## Code Review Feedback Addressed
### From @disintegrator:
- ✅ Renamed \`--header-name\` to \`--header\` for simplicity
- ✅ Changed default from \`Gram-Apikey\` to \`Authorization\` (HTTP
standard)
- ✅ Fixed Downloads directory handling for Linux (checks existence,
falls back to \`.\`)
- ✅ Made browser opening non-fatal in Cursor (shows manual URL on
failure)
- ✅ Removed header name derivation logic (was making unfounded
assumptions)
- ✅ Reverted out-of-scope \`status.go\` changes
### From @qstearns:
- 📝 Note: Support for non-authentication headers can be added in a
follow-up PR if needed
## Test Plan
- [x] Build CLI: \`mise build:cli\`
- [x] Run linters: \`mise lint:cli\`
- [x] Test automatic lookup: \`gram install claude-code --toolset
<toolset-slug>\`
- [x] Test manual URL: \`gram install claude-code --mcp-url
https://mcp.getgram.ai/org/proj/env --api-key test-key\`
- [x] Test env var substitution: \`gram install claude-code --toolset
<slug> --env-var MY_VAR\`
- [x] Test local env var detection: Set env var locally and verify
actual value is used
- [x] Test Linux compatibility: Verify Downloads directory fallback
- [x] Test Cursor graceful degradation: Verify manual URL shown on
browser failure
- [x] Verify generated configs have correct format for each client
- [x] Test with custom \`--name\` flag
- [x] Test custom \`--header\` flag
- [x] Verify Claude Code config works by restarting and checking MCP
servers load
- [x] Test Claude Desktop \`.dxt\` file installation
- [x] Test Cursor browser-based installation
- [x] Test Gemini CLI configuration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Sagar Batchu <simplesagar@users.noreply.github.com>
Co-authored-by: chasecrumbaugh <chasecrumbaugh4@gmail.com>
Co-authored-by: Georges Haidar <ghaidar0@gmail.com>1 parent 69a36c0 commit 31e555b
File tree
30 files changed
+9648
-7027
lines changed- .changeset
- cli/internal
- api
- app
- claudecode
- mcp
- workflow
- server
- design/toolsets
- gen
- http
- cli/gram
- toolsets
- client
- server
- variations/client
- toolsets
- internal/toolsets
30 files changed
+9648
-7027
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
0 commit comments