Skip to content

DRAFT - initial stab at MCP support #460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion anthropic.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (stream *anthropicStreamReader) processLines() (openai.ChatCompletionStream
}
writeErr := stream.errAccumulator.Write(noSpaceLine)
if writeErr != nil {
return *new(openai.ChatCompletionStreamResponse), fmt.Errorf("ollamaStreamReader.processLines: %w", writeErr)
return *new(openai.ChatCompletionStreamResponse), fmt.Errorf("anthropicStreamReader.processLines: %w", writeErr)
}
emptyMessagesCount++
if emptyMessagesCount > stream.emptyMessagesLimit {
Expand Down
19 changes: 19 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ var help = map[string]string{
"theme": "Theme to use in the forms. Valid units are: 'charm', 'catppuccin', 'dracula', and 'base16'",
"show-last": "Show the last saved conversation.",
"editor": "Edit the prompt in your $EDITOR. Only taken into account if no other args and if STDIN is a TTY.",
"mcp-servers": "List of MCP servers to include",
"mcp-servers-from": "Path to a JSON file containing additional MCP server configurations",
"list-mcp-servers": "List all available MCP servers",
"list-mcp-tools": "List all available tools from enabled MCP servers",
"all-mcp-servers": "Enable all configured MCP servers",
"default-mcp-servers": "Default MCP servers to use if none specified",
}

// Model represents the LLM model used in the API call.
Expand Down Expand Up @@ -180,11 +186,24 @@ type Config struct {
Delete []string
DeleteOlderThan time.Duration
User string
MCPServers map[string]MCPServerConfig `yaml:"mcp-servers"`
MCPServersFrom string `yaml:"mcp-servers-from"`
DefaultMCPServers []string `yaml:"default-mcp-servers"`
UseMCPServers []string
ListMCPServers bool
ListMCPTools bool
UseAllMCPServers bool

openEditor bool
cacheReadFromID, cacheWriteToID, cacheWriteToTitle string
}

// MCPServerConfig holds configuration for an MCP server
type MCPServerConfig struct {
Command string `yaml:"command"`
Args []string `yaml:"args"`
}

func ensureConfig() (Config, error) {
var c Config
sp, err := xdg.ConfigFile(filepath.Join("mods", "mods.yml"))
Expand Down
14 changes: 14 additions & 0 deletions config_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,17 @@ apis:
deepseek-code:
aliases: ["ds-code"]
max-input-chars: 384000

# {{ index .Help "mcp-servers" }}
mcp-servers:
# sqlite:
# command: "uvx"
# args: ["mcp-server-sqlite", "--db-path", "/Users/YOUR_USERNAME/test.db"]

# {{ index .Help "mcp-servers-from" }}
mcp-servers-from: ""
# mcp-servers-from: "~/.mcp-servers.json"

# {{ index .Help "default-mcp-servers" }}
default-mcp-servers: []
# - sqlite
Binary file added examples/mcp-example-sqlite.db
Binary file not shown.
17 changes: 17 additions & 0 deletions examples/mcp_servers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"mcpServers": {
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
]
},
"fetch": {
"command": "uvx",
"args": [
"mcp-server-fetch"
]
}
}
}
73 changes: 73 additions & 0 deletions features.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,76 @@ mods --delete='naturals' --delete='a2e2'

Keep in mind that these operations are not reversible.
You can repeat the delete flag to delete multiple conversations at once.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you remove this extra line, please?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure - in the future, is there a formatter/checker that can detect these sorts of style things for me?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's https://github.com/DavidAnson/markdownlint, and if you use VS Code you can use their extension and it renders a yellow wiggle underline that you can either hover over or list in the "problems" view, or cycle through problems with F8, like this:
image


## Using MCP

Mods can be optionally be used as an MCP client.

### Using an MCP servers.json

You can configure MCP servers in the settings file, or on the command line by passing a `--mcp-servers-from` flag.

```bash
mods --mcp-servers-from=~/.config/mcp/servers.json --list-mcp-servers
```

Or in your config file:

```yaml
mcp-servers-from: ~/.config/mcp/servers.json
```

### Specifying MCP servers in the mods settings
Comment on lines +135 to +141
Copy link
Contributor

@philippgille philippgille Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth describing the expected file schema or show an example file somewhere in this section, because it's not described anywhere yet, right?

Suggested change
Or in your config file:
```yaml
mcp-servers-from: ~/.config/mcp/servers.json
```
### Specifying MCP servers in the mods settings
Or in your config file:
```yaml
mcp-servers-from: ~/.config/mcp/servers.json
```
That file should look like this:
```json
{
"mcpServers": {
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"]
}
}
}
```
### Specifying MCP servers in the mods settings


In the config file, you can specify the path to the MCP servers file, or a list of servers to use.

use `mods --settings` to edit the settings file.

```yaml
mcp-servers:
sqlite:
command: "uvx"
args: ["mcp-server-sqlite", "--db-path", "/Users/YOUR_USERNAME/test.db"]
fetch:
command: "uvx"
args: ["mcp-server-fetch"]
```

### Listing MCP servers

You can list the MCP servers you have configured by running:

```bash
mods --list-mcp-servers
```

### Default MCP servers

By default, configured MCP servers will not be exposed to models. You can specify default MCP servers to always enable the settings file.

```yaml
default-mcp-servers:
- sqlite
```

### Enabling MCP severs

to enable MCP servers on the command line, you can use the `--mcp-servers`, `-T` flag.

```bash
mods --mcp-servers=fetch
```

### Listing MCP tools

You can list the MCP tools you have configured by running:

```bash
mods --list-mcp-tools
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you remove these extra lines to the end?





5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/charmbracelet/mods

go 1.21
go 1.23

toolchain go1.24.0
Comment on lines +3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the latest toolchain required? If not, I think this can stay at whichever Go version is minimally required by the dependencies. mcp-go only requires Go 1.23. People are still free to use Go 1.24 compiler to build this, but they won't have to.
(Technically, since Go 1.21, Go checks for the version in the go.mod file and implicitly downloads and uses newer versions behind the scenes if necessary, so theoretically users could still just have Go 1.23 installed. But I think it's still better/cleaner to just declare the version that's actually required, at least as long as it's officially supported)
More info on that: https://go.dev/doc/go1.21#tools

Suggested change
go 1.23
toolchain go1.24.0
go 1.23.0


require (
github.com/adrg/xdg v0.5.3
Expand All @@ -20,6 +22,7 @@ require (
github.com/cohere-ai/cohere-go/v2 v2.12.4
github.com/jmoiron/sqlx v1.4.0
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mark3labs/mcp-go v0.11.2
github.com/mattn/go-isatty v0.0.20
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/roff v0.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mark3labs/mcp-go v0.11.2 h1:mCxWFUTrcXOtJIn9t7F8bxAL8rpE/ZZTTnx3PU/VNdA=
github.com/mark3labs/mcp-go v0.11.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
Expand Down
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ var (
return listConversations(config.Raw)
}

if config.ListMCPServers {
return listMCPServers()
}

if config.ListMCPTools {
return listMCPTools()
}

if len(config.Delete) > 0 {
return deleteConversations()
}
Expand Down Expand Up @@ -265,6 +273,11 @@ func initFlags() {
flags.BoolVar(&config.ListRoles, "list-roles", config.ListRoles, stdoutStyles().FlagDesc.Render(help["list-roles"]))
flags.StringVar(&config.Theme, "theme", "charm", stdoutStyles().FlagDesc.Render(help["theme"]))
flags.BoolVarP(&config.openEditor, "editor", "e", false, stdoutStyles().FlagDesc.Render(help["editor"]))
flags.StringVar(&config.MCPServersFrom, "mcp-servers-from", config.MCPServersFrom, stdoutStyles().FlagDesc.Render(help["mcp-servers-from"]))
flags.BoolVar(&config.ListMCPServers, "list-mcp-servers", false, stdoutStyles().FlagDesc.Render(help["list-mcp-servers"]))
flags.BoolVar(&config.ListMCPTools, "list-mcp-tools", false, stdoutStyles().FlagDesc.Render(help["list-mcp-tools"]))
flags.StringArrayVarP(&config.UseMCPServers, "mcp-servers", "T", config.UseMCPServers, stdoutStyles().FlagDesc.Render(help["mcp-servers"]))
flags.BoolVarP(&config.UseAllMCPServers, "all-mcp-servers", "A", config.UseAllMCPServers, stdoutStyles().FlagDesc.Render(help["all-mcp-servers"]))
flags.Lookup("prompt").NoOptDefVal = "-1"
flags.SortFlags = false

Expand Down Expand Up @@ -303,6 +316,7 @@ func initFlags() {
"continue",
"continue-last",
"reset-settings",
"list-mcp-servers",
)
}

Expand Down Expand Up @@ -735,6 +749,8 @@ func isNoArgs() bool {
!config.ShowHelp &&
!config.List &&
!config.ListRoles &&
!config.ListMCPServers &&
!config.ListMCPTools &&
!config.Dirs &&
!config.Settings &&
!config.ResetSettings
Expand All @@ -752,6 +768,7 @@ func askInfo() error {

if config.ContinueLast {
found, err := db.FindHEAD()

if err == nil && found != nil && found.Model != nil {
config.Model = *found.Model
}
Expand Down
Loading