Skip to content

Commit 0350051

Browse files
authored
.mcpd.toml stores required env and args (#55)
1 parent 271afdd commit 0350051

File tree

9 files changed

+943
-131
lines changed

9 files changed

+943
-131
lines changed

cmd/add.go

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ func NewAddCmd(baseCmd *cmd.BaseCmd, opt ...cmdopts.CmdOption) (*cobra.Command,
4747
cobraCommand := &cobra.Command{
4848
Use: "add <server-name>",
4949
Short: "Adds an MCP server dependency to the project.",
50-
Long: c.longDescription(),
51-
RunE: c.run,
50+
Long: `Adds an MCP server dependency to the project.
51+
mcpd will search the registry for the named server and attempt to return information on the version specified,
52+
or 'latest' if no version specified.`,
53+
RunE: c.run,
5254
}
5355

5456
cobraCommand.Flags().StringVar(
@@ -82,13 +84,6 @@ func NewAddCmd(baseCmd *cmd.BaseCmd, opt ...cmdopts.CmdOption) (*cobra.Command,
8284
return cobraCommand, nil
8385
}
8486

85-
// longDescription returns the long version of the command description.
86-
func (c *AddCmd) longDescription() string {
87-
return `Adds an MCP server dependency to the project.
88-
mcpd will search the registry for the server and attempt to return information on the version specified,
89-
or 'latest' if no version specified.`
90-
}
91-
9287
// run is configured (via NewAddCmd) to be called by the Cobra framework when the command is executed.
9388
// It may return an error (or nil, when there is no error).
9489
func (c *AddCmd) run(cmd *cobra.Command, args []string) error {
@@ -97,9 +92,6 @@ func (c *AddCmd) run(cmd *cobra.Command, args []string) error {
9792
}
9893

9994
name := strings.TrimSpace(args[0])
100-
if name == "" {
101-
return fmt.Errorf("server name cannot be empty")
102-
}
10395

10496
logger, err := c.Logger()
10597
if err != nil {
@@ -151,7 +143,13 @@ func (c *AddCmd) run(cmd *cobra.Command, args []string) error {
151143
if err != nil {
152144
return err
153145
}
154-
logger.Debug("Server added", "name", name, "version", entry.PackageVersion(), "tools", entry.Tools)
146+
logger.Debug(
147+
"Server added",
148+
"name", name,
149+
"package", entry.Package,
150+
"version", entry.PackageVersion(),
151+
"tools", entry.Tools,
152+
)
155153

156154
// Print the package info.
157155
if err = c.packagePrinter.PrintPackage(pkg); err != nil {
@@ -228,10 +226,15 @@ func parseServerEntry(
228226
}
229227
runtimePackageVersion := fmt.Sprintf("%s::%s@%s", selectedRuntime, runtimeSpecificName, v)
230228

229+
envs := packages.FilterArguments(pkg.Arguments, packages.EnvVar, packages.Required)
230+
args := packages.FilterArguments(pkg.Arguments, packages.Argument, packages.Required)
231+
231232
return config.ServerEntry{
232-
Name: pkg.ID,
233-
Package: runtimePackageVersion,
234-
Tools: requestedTools,
233+
Name: pkg.ID,
234+
Package: runtimePackageVersion,
235+
Tools: requestedTools,
236+
RequiredArgs: args.Names(),
237+
RequiredEnvVars: envs.Names(),
235238
}, nil
236239
}
237240

internal/config/types.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package config
22

3-
import "strings"
3+
import (
4+
"strings"
5+
)
46

57
var (
68
_ Provider = (*DefaultLoader)(nil)
@@ -47,6 +49,12 @@ type ServerEntry struct {
4749
// Tools lists the names of the tools which should be allowed on this server.
4850
// e.g. 'create_repository'
4951
Tools []string `toml:"tools"`
52+
53+
// RequiredEnvVars captures any environment variables required to run the server.
54+
RequiredEnvVars []string `toml:"required_env,omitempty"`
55+
56+
// RequiredArgs captures any command line args required to run the server.
57+
RequiredArgs []string `toml:"required_args,omitempty"`
5058
}
5159

5260
type serverKey struct {

internal/packages/argument.go

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package packages
22

33
import (
4+
"maps"
45
"regexp"
6+
"slices"
57
)
68

79
const (
@@ -27,51 +29,44 @@ type ArgumentMetadata struct {
2729
VariableType VariableType `json:"type"`
2830
}
2931

30-
func (a *Arguments) byVariableType(vt VariableType) map[string]ArgumentMetadata {
31-
args := *a
32-
result := make(map[string]ArgumentMetadata, len(args))
33-
for name, arg := range args {
34-
if arg.VariableType == vt {
35-
result[name] = arg
36-
}
37-
}
38-
return result
32+
// FilterBy allows filtering of Arguments using predicates.
33+
// All predicates must be true in order for an argument to be included in the results.
34+
func (a Arguments) FilterBy(predicate ...func(name string, data ArgumentMetadata) bool) Arguments {
35+
return FilterArguments(a, predicate...)
3936
}
4037

41-
func (a *Arguments) EnvVars() Arguments {
42-
return a.byVariableType(VariableTypeEnv)
38+
// Names returns the names of the Arguments.
39+
func (a Arguments) Names() []string {
40+
return slices.Collect(maps.Keys(a))
4341
}
4442

45-
func (a *Arguments) EnvVarNames() []string {
46-
var ns []string
47-
for k, v := range *a {
48-
if v.IsEnvironmentVariable() {
49-
ns = append(ns, k)
43+
// FilterArguments allows Arguments to be filtered using any number of predicates.
44+
// All predicates must be true in order for an argument to be included in the results.
45+
func FilterArguments(args Arguments, predicate ...func(name string, data ArgumentMetadata) bool) Arguments {
46+
result := make(Arguments)
47+
next:
48+
for name, arg := range args {
49+
for _, p := range predicate {
50+
if !p(name, arg) {
51+
continue next
52+
}
5053
}
54+
result[name] = arg
5155
}
52-
return ns
53-
}
54-
55-
func (a *Arguments) Args() Arguments {
56-
return a.byVariableType(VariableTypeArg)
56+
return result
5757
}
5858

59-
func (a *Arguments) ArgNames() []string {
60-
var ns []string
61-
for k, v := range *a {
62-
if v.IsCommandLineArgument() {
63-
ns = append(ns, k)
64-
}
65-
}
66-
return ns
59+
// Required is a predicate that requires the argument is required.
60+
func Required(_ string, data ArgumentMetadata) bool {
61+
return data.Required
6762
}
6863

69-
// IsEnvironmentVariable returns true if this argument is primarily used as an environment variable
70-
func (am ArgumentMetadata) IsEnvironmentVariable() bool {
71-
return am.VariableType == VariableTypeEnv
64+
// EnvVar is a predicate that requires the argument is an environment variable.
65+
func EnvVar(_ string, data ArgumentMetadata) bool {
66+
return data.VariableType == VariableTypeEnv
7267
}
7368

74-
// IsCommandLineArgument returns true if this argument is primarily used as a command line argument
75-
func (am ArgumentMetadata) IsCommandLineArgument() bool {
76-
return am.VariableType == VariableTypeArg
69+
// Argument is a predicate that requires the argument is a command line argument.
70+
func Argument(_ string, data ArgumentMetadata) bool {
71+
return data.VariableType == VariableTypeArg
7772
}

0 commit comments

Comments
 (0)