Skip to content

Commit 1766f3c

Browse files
committed
Merge branch 'main' into install-lots-of-servers
2 parents d8b850f + 9f500ff commit 1766f3c

4 files changed

Lines changed: 139 additions & 11 deletions

File tree

aws/aws-mcp-cli-commands/src/main/java/software/amazon/smithy/java/aws/mcp/cli/commands/AddAwsServiceBundle.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,25 @@ public class AddAwsServiceBundle extends AbstractAddBundle {
3434
@Option(names = {"-b", "--blocked-apis"}, description = "List of APIs to hide in the MCP server")
3535
protected Set<String> blockedApis;
3636

37-
@Option(names = "--include-write-apis",
38-
description = "Include write APIs in the MCP server")
39-
Boolean includeWriteApis;
37+
@Option(names = "--read-only-apis",
38+
description = "Include read only APIs in the MCP server",
39+
defaultValue = "true")
40+
protected boolean readOnlyApis;
4041

4142
@Override
4243
protected CliBundle getNewToolConfig() {
4344
var bundleBuilder = AwsServiceBundler.builder()
4445
.serviceName(awsServiceName);
45-
//If nothing is specified then default to only readOnlyOperations.
46+
if (readOnlyApis) {
47+
// Include read-only apis unless disabled
48+
bundleBuilder.readOnlyOperations();
49+
}
4650
if (allowedApis != null) {
4751
// User explicitly specified allowed APIs - use only those
4852
bundleBuilder.exposedOperations(allowedApis);
49-
} else if (includeWriteApis == null || !includeWriteApis) {
50-
// Default case or explicit false - read-only operations
51-
bundleBuilder.readOnlyOperations();
5253
}
53-
// Always apply blocked operations if specified
5454
if (blockedApis != null) {
55+
// Always apply blocked operations if specified
5556
bundleBuilder.blockedOperations(blockedApis);
5657
}
5758
var bundle = bundleBuilder.build().bundle();

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jmh = "0.7.3"
66
test-logger-plugin = "4.0.0"
77
spotbugs = "6.0.22"
88
spotless = "7.0.4"
9-
smithy-gradle-plugins = "1.2.0"
9+
smithy-gradle-plugins = "1.3.0"
1010
assertj = "3.27.3"
1111
jackson = "2.19.0"
1212
netty = "4.2.2.Final"

mcp/mcp-cli-api/src/main/java/software/amazon/smithy/java/mcp/cli/ConfigUtils.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.nio.file.Path;
1616
import java.nio.file.Paths;
1717
import java.nio.file.StandardOpenOption;
18+
import java.util.ArrayList;
1819
import java.util.Comparator;
1920
import java.util.HashMap;
2021
import java.util.LinkedHashMap;
@@ -53,8 +54,12 @@ private ConfigUtils() {}
5354
.serializeTypeInDocuments(false)
5455
.build();
5556

57+
private static final String OS = System.getProperty("os.name").toLowerCase();
58+
private static final boolean WINDOWS = OS.contains("windows");
59+
5660
private static final Path CONFIG_DIR = resolveFromHomeDir(".config", "smithy-mcp");
5761
private static final Path BUNDLE_DIR = CONFIG_DIR.resolve("bundles");
62+
private static final Path SHIMS_DIR = CONFIG_DIR.resolve("mcp-servers");
5863
private static final Path CONFIG_PATH = CONFIG_DIR.resolve("config.json");
5964

6065
private static final DefaultConfigProvider DEFAULT_CONFIG_PROVIDER;
@@ -63,6 +68,7 @@ private ConfigUtils() {}
6368
try {
6469
ensureDirectoryExists(CONFIG_DIR);
6570
ensureDirectoryExists(BUNDLE_DIR);
71+
ensureDirectoryExists(SHIMS_DIR);
6672
} catch (IOException e) {
6773
throw new RuntimeException(e);
6874
}
@@ -169,6 +175,8 @@ public static void removeMcpBundle(Config currentConfig, String bundleName) thro
169175
updateConfig(newConfig);
170176
var bundleFile = getBundleFileLocation(bundleName);
171177
Files.deleteIfExists(bundleFile);
178+
// Remove wrapper script if it exists
179+
removeWrapperScript(bundleName);
172180
}
173181

174182
public static void addToClientConfigs(Config config, String name, Set<String> clients, McpServerConfig serverConfig)
@@ -333,4 +341,112 @@ private static String captureProcessOutput(Process process) throws IOException {
333341
return in.lines().collect(Collectors.joining(System.lineSeparator()));
334342
}
335343
}
344+
345+
public static void createWrapperScript(String bundleName) throws IOException {
346+
Path scriptPath = SHIMS_DIR.resolve(bundleName);
347+
String scriptContent = createScriptContent(bundleName);
348+
349+
Files.writeString(scriptPath, scriptContent, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
350+
scriptPath.toFile().setExecutable(true);
351+
}
352+
353+
private static String createScriptContent(String bundleName) {
354+
if (WINDOWS) {
355+
return "@echo off\nmcp-registry start-server " + bundleName + " %*\n";
356+
} else {
357+
return "#!/bin/bash\nexec mcp-registry start-server " + bundleName + " \"$@\"\n";
358+
}
359+
}
360+
361+
public static void removeWrapperScript(String bundleName) throws IOException {
362+
Path scriptPath = SHIMS_DIR.resolve(bundleName);
363+
Files.deleteIfExists(scriptPath);
364+
}
365+
366+
public static boolean isMcpServersDirInPath() {
367+
String pathEnv = System.getenv("PATH");
368+
if (pathEnv == null) {
369+
return false;
370+
}
371+
372+
String shimsDirStr = SHIMS_DIR.toAbsolutePath().toString();
373+
return pathEnv.contains(shimsDirStr);
374+
}
375+
376+
public static void ensureMcpServersDirInPath() {
377+
if (isMcpServersDirInPath()) {
378+
return;
379+
}
380+
381+
String shimsDirStr = SHIMS_DIR.toAbsolutePath().toString();
382+
383+
if (WINDOWS) {
384+
printWindowsPathInstructions(shimsDirStr);
385+
} else {
386+
if (!tryAddToShellConfigs(shimsDirStr)) {
387+
printUnixPathInstructions(shimsDirStr);
388+
}
389+
}
390+
}
391+
392+
private static boolean tryAddToShellConfigs(String shimsDirStr) {
393+
var pathExport = "export PATH=\"" + shimsDirStr + ":$PATH\"";
394+
var comment = "# Added by smithy-mcp";
395+
var configFiles = List.of(".zshrc", ".bashrc", ".profile", ".bash_profile");
396+
397+
String addedTo = null;
398+
399+
for (var configFile : configFiles) {
400+
var configPath = resolveFromHomeDir(configFile);
401+
402+
if (Files.exists(configPath) && Files.isWritable(configPath)) {
403+
try {
404+
// Check if already present
405+
var lines = new ArrayList<>(Files.readAllLines(configPath, StandardCharsets.UTF_8));
406+
boolean alreadyPresent = lines.stream()
407+
.anyMatch(line -> line.contains(shimsDirStr)
408+
&& line.contains("PATH")
409+
&& !(line.trim().startsWith("#")));
410+
411+
if (!alreadyPresent) {
412+
// Add the export statement
413+
lines.add("");
414+
lines.add(comment);
415+
lines.add(pathExport);
416+
417+
Files.write(configPath,
418+
lines,
419+
StandardCharsets.UTF_8,
420+
StandardOpenOption.WRITE,
421+
StandardOpenOption.TRUNCATE_EXISTING);
422+
423+
System.out.println("Added mcp-servers directory to PATH in " + configFile);
424+
addedTo = configFile;
425+
}
426+
} catch (IOException e) {
427+
// Continue to next config file if this one fails
428+
}
429+
}
430+
}
431+
432+
if (addedTo != null) {
433+
System.out.println("Please restart your shell or run 'source ~/" + configFiles + "' to reload your PATH");
434+
return true;
435+
}
436+
return false;
437+
}
438+
439+
private static void printWindowsPathInstructions(String shimsDirStr) {
440+
System.out.println("\nTo use the installed bundle as a command, add the mcp-servers directory to your PATH:");
441+
System.out.println(" set PATH=" + shimsDirStr + ";%PATH%");
442+
System.out.println("Or permanently add it through System Properties > Environment Variables");
443+
System.out.println();
444+
}
445+
446+
private static void printUnixPathInstructions(String shimsDirStr) {
447+
System.out.println("\nTo use the installed bundle as a command, add the mcp-servers directory to your PATH:");
448+
System.out.println(" export PATH=\"" + shimsDirStr + ":$PATH\"");
449+
System.out.println("Add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.) to make it permanent");
450+
System.out.println();
451+
}
336452
}

mcp/mcp-cli/src/main/java/software/amazon/smithy/java/mcp/cli/commands/InstallBundle.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,22 @@ protected void execute(ExecutionContext context) throws IOException {
4444
for (var name : names) {
4545
var bundle = registry.getMcpBundle(name);
4646
ConfigUtils.addMcpBundle(config, name, bundle);
47-
var command = "mcp-registry";
48-
var args = List.of("start-server", name);
47+
48+
var command = name;
49+
List<String> args = null;
50+
boolean shouldCreateWrapper = true;
51+
4952
if (bundle.getValue() instanceof GenericBundle genericBundle && genericBundle.isExecuteDirectly()) {
5053
command = genericBundle.getRun().getExecutable();
5154
args = genericBundle.getRun().getArgs();
55+
shouldCreateWrapper = false;
56+
}
57+
58+
if (shouldCreateWrapper) {
59+
ConfigUtils.createWrapperScript(name);
60+
ConfigUtils.ensureMcpServersDirInPath();
5261
}
62+
5363
var newClientConfig = McpServerConfig.builder()
5464
.command(command)
5565
.args(args)
@@ -61,6 +71,7 @@ protected void execute(ExecutionContext context) throws IOException {
6171
ConfigUtils.addToClientConfigs(config, name, clients, newClientConfig);
6272

6373
System.out.println("Successfully installed " + name);
74+
6475
if (print) {
6576
System.out.println("You can add the following to your MCP Servers config to use " + name);
6677
System.out.println(newClientConfig);

0 commit comments

Comments
 (0)