Skip to content

First cut of the smithy-mcp CLI #677

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 11 commits into
base: main
Choose a base branch
from
Open

First cut of the smithy-mcp CLI #677

wants to merge 11 commits into from

Conversation

adwsingh
Copy link
Contributor

Issue #, if available:

Description of changes:

Currently supports two commands,

  1. configure -> This has further subcommands which allow adding bundles to the config.
  2. start-server -> This starts an MCP server for a given pre-configured bundle.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@adwsingh adwsingh force-pushed the adwsingh/mcp-cli branch 4 times, most recently from ebda0cc to 5dd3763 Compare April 30, 2025 22:11
Comment on lines 125 to 132
private static String loadModel(String path) {
try (var reader = new BufferedReader(new InputStreamReader(
Objects.requireNonNull(AwsServiceBundler.class.getResourceAsStream(path)),
StandardCharsets.UTF_8))) {
return reader.lines().collect(Collectors.joining());
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
throw new RuntimeException(e);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We should hoist this to Bundler as a protected final method. I've already messed this up twice and I'll keep messing it up every time I rewrite it.

execute(config);
return 0;
} catch (IllegalArgumentException e) {
System.out.println("Invalid input : [" + e.getMessage() + "]");
Copy link
Contributor

Choose a reason for hiding this comment

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

  • Why println here and LOG in the Exception arm?
  • Please don't discard the stack trace. Do e.printStackTrace(System.out) if you must, although we should likely be using the logger here (unless you intended for this to go to the mcp client's logs, in which case stderr is a better choice)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So the idea was we only emit messages for errors and only print stack traces. I didn't want to log a stack trace for wrong inputs.

I think I need to do a second pass on logging, and also configure the logger. Will cleanup logging and println in the CLIs in a followup.


@Override
public String[] getVersion() throws Exception {
return new String[] {IoUtils.readUtf8Resource(VersionProvider.class, "VERSION").trim()};
Copy link
Contributor

Choose a reason for hiding this comment

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

formatting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried adding a space but Spotless keeps changing it back to this.

@adwsingh adwsingh force-pushed the adwsingh/mcp-cli branch from 5dd3763 to 7e272ef Compare May 7, 2025 00:49
@adwsingh adwsingh requested a review from rhernandez35 May 7, 2025 01:25

extra["displayName"] = "Smithy :: Java :: Model Bundler"
extra["moduleName"] = "software.amazon.smithy.java.modelbundle.api"
extra["displayName"] = "Smithy :: Java :: MCP Bundler"
Copy link
Contributor

Choose a reason for hiding this comment

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

Leave this in the current module. There's nothing intrinsically tied to MCP in this module, and I want to use the bundle format to drive smithy-call in the future.

var endpoint = URI.create(Objects.requireNonNull(serviceMetadata.getEndpoints().get(input.getAwsRegion()),
"no endpoint for region " + input.getAwsRegion()));

try (var sdkCredentialsProvider = ProfileCredentialsProvider.create(input.getAwsProfileName())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

sdkCredentialsProvider will be closed when we return from this method but the reference will live in identityResolver. I don't think we want the try-with-resources here.


public abstract Bundle bundle();

protected String loadModel(String path) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be static or final

var modelAssemble = new ModelAssembler().putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
.addUnparsedModel("bundle.json", bundle.getModel());
var additionalInput = bundle.getAdditionalInput();
if (additionalInput != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We're only running the streaming operation redaction logic when there's additional inputs, but we should always be doing it

Comment on lines +55 to +56
skipOperation = true;
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

Did checkstyle complain about using a labeled break or is this a personal style choice?


if (!CONFIG_PATH.toFile().exists()) {
// Create an empty JSON object as the default config
Files.write(CONFIG_PATH, toJson(DEFAULT_CONFIG_PROVIDER.getConfig()), StandardOpenOption.CREATE_NEW);
Copy link
Contributor

Choose a reason for hiding this comment

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

This will throw if the file was created in between us check if it existed and us trying to create it

* @throws IOException If there's an error writing to the file
*/
public static void updateConfig(Config config) throws IOException {
Files.write(CONFIG_PATH, toJson(config), StandardOpenOption.CREATE_NEW);
Copy link
Contributor

Choose a reason for hiding this comment

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

CREATE_NEW causes this to throw if the file already exists, so this will (almost?) always fail


import software.amazon.smithy.java.mcp.cli.model.Config;

public class EmptyDefaultConfigProvider implements DefaultConfigProvider {
Copy link
Contributor

Choose a reason for hiding this comment

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

final

.putSupportedAuthSchemes(StaticAuthSchemeResolver.staticScheme(authScheme));
public <C extends Client, B extends Client.Builder<C, B>> B configureClient(B clientBuilder) {
clientBuilder.addInterceptor(new AwsServiceClientInterceptor(serviceMetadata, authScheme));
clientBuilder.endpointResolver(EndpointResolver.staticEndpoint("http://dummyurl.com"));
Copy link
Contributor

Choose a reason for hiding this comment

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

use localhost. let's avoid using anything that could result in traffic being sent to a domain squatter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants