Skip to content

Conversation

@Naatan
Copy link
Contributor

@Naatan Naatan commented Apr 10, 2025

@Naatan Naatan changed the title Added highly experimental state-mcp Added state-mcp Jul 11, 2025
# Conflicts:
#	go.mod
@Naatan
Copy link
Contributor Author

Naatan commented Jul 11, 2025

@samueld-activator flagged you for review mainly for the high level architecture, much of the actual business logic I suggest you defer to Mitchell for now.

Of main interest to you will be:

https://github.com/ActiveState/cli/pull/3671/files#diff-e49aff4bd82f3519a62eed2b59080de67be9fabe342a5d1962512dba2437a388

@mitchell-as This is introducing the state-mcp executable with basic handling for tool calling. I'm not entirely happy with how primer and the output package are being treated, but addressing that properly would be a significantly bigger refactor that I don't want to get into now.

Copy link
Collaborator

@mitchell-as mitchell-as left a comment

Choose a reason for hiding this comment

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

There are failing unit tests that need to be addressed: https://github.com/ActiveState/cli/actions/runs/16231227928/job/45834291877?pr=3671#step:14:1021.

I've reviewed everything except the 'state-mcp' folder. I will do so after either Samuel weighs in on the architecture, or my other comments are addressed.

Comment on lines +19 to +22
func Categories() ToolCategories {
return ToolCategories{
ToolCategoryDebug,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Usually we like to return pointers to avoid copies each time this function is called.

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've gotten into the habit of only using pointers when I intend to track the underlying type across different areas of responsibility. I don't believe we ever had any rule around this.

I'd have preferred to make this a constant, but Go is a little weird about what things can be constants.

Comment on lines 64 to 69
defer func() {
if rc := recover(); rc != nil {
logging.Error("Recovered from panic: %v", rc)
fmt.Printf("Recovered from panic: %v\n", rc)
}
}()
Copy link
Collaborator

Choose a reason for hiding this comment

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

None of the other runners have this. This seems like a quick, cheap fix that hides a more serious problem. Ideally we would utilize defensive programming to avoid this scenario.

Perhaps this is leftover from a debug session to identify the problem? In that case, please remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching. I don't recall why I did this, probably was trying to debug.

projectRegex := regexp.MustCompile(fmt.Sprintf("(?m:(^project:\\s*%s))", ProjectURLRe))
lockString := fmt.Sprintf("%s@%s", branch, version)
lockUpdate := []byte(fmt.Sprintf("${1}\nlock: %s", lockString))
lockUpdate := []byte(fmt.Sprintf(`${1}\nlock: %s`, lockString))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see why this was changed. Now there will be no newline in the string, but instead a literal '' followed by an 'n'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I don't recall. Some of these changes go back months. Probably had a previous change that I reverted.

// expressed in its own file.
package hello

import (
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why there are changes in this file. It's supposed to be a pretty complete example for a runner. These changes are stripping it down and minimizing its usefulness.

Please don't tell me you had an AI produce a good chunk of this PR and I'm the first one reviewing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it required me to satisfy project dependencies that are inappropriate in an MCP context. I could have went the extra mile and mocked them anyway, but in reviewing this I didn't feel like it added any real value for the purposes of showing an example runner. I don't mind bringing it back and mocking it if you disagree.

No I did not have AI generate any significant parts. I use it mainly as a glorified auto completer. I will say I've found it useful to generate larger bits of logic when working with technologies I'm unfamiliar with, but that's obviously not the case here.

Comment on lines +72 to +74
w := NewWriteProxy(f.cfg.OutWriter, func(p []byte) {
f.history.Print = append(f.history.Print, string(p))
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wish there was a way to not have to construct this every single time this function is called. I can't think of one though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I went through a couple of different ideas and this one felt the least awkward, but definitely still awkward.

This goes back to my earlier comment; I'd like to address this better but I don't think I can reasonably do so without blowing up the scope of the mcp story significantly, so for now it'll have to be an annoyance that we get to later (which is starting to become a bit of a list wrt the output package..).

_, err = HomeDir()
assert.Error(t, err)
assert.Contains(t, err.Error(), "HOME environment variable is unset")
assert.NoError(t, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel like this change negates the value of the test since you've changed init() to check for environment variables.

Please consider either removing the test, or changing it to be more useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is testing that we get our home dir with no env vars set, I don't think it makes sense to remove it.

Note the error tells you to set your HOME dir as it's the one way a user could address this issue. That doesn't mean that the HOME dir not being set is the cause of the issue.

Comment on lines +46 to +49
u, err := user.Current()
if err == nil {
return u.HomeDir, nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the rationale for this change? This could potentially be very significant, as it affects every installation. I'm not very comfortable seeing this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This addresses running state tool or state-mcp without any environment variables. In the case of mcp it seems common that servers are ran without any env vars, and so relying on HOME will break them. user.Current() uses low level system bindings to retrieve the user info, rather than env vars. This is what all the refactoring in user and storage is about, and why configdir was removed.

This will be required for state-mcp, and I recall occasionally seeing rollbar errors about HOME not being set, so it will help there too.

I'm not too concerned with these changes, either way we rely on the system to tell us the home dir of the current user, it's just through a different channel. And configdir is a very dumb library, I looked into fixing its issues and found that it's so simplistic I was better off just copying the paths it used.

"github.com/ActiveState/cli/internal/constants"
"github.com/ActiveState/cli/internal/osutils/user"
"github.com/google/uuid"
"github.com/shibukawa/configdir"
Copy link
Collaborator

Choose a reason for hiding this comment

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

May I ask why we are no longer using this dependency and instead creating our own version?

Naatan added 4 commits July 15, 2025 09:47
Not sure why this is suddenly breaking. The cast library was updated, but the code paths for the errors I'm seeing were not..
@Naatan Naatan requested a review from mitchell-as July 15, 2025 22:04
mitchell-as
mitchell-as previously approved these changes Jul 16, 2025
Copy link
Collaborator

@mitchell-as mitchell-as left a comment

Choose a reason for hiding this comment

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

Looks good to me!

func TestServerHello(t *testing.T) {
t.Skip(`
Fails due to state-svc not being detected when run as regular test,
works when running with debugger. Problem for another day.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have a ticket to track this? Otherwise we might lose track of it.

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'm going to leave it alone for now as this feels like a growing pain. This really isn't even a unit test if it relies on state-svc. It's just that we currently don't have a way to end to end test MCP. That'll be its own thing down the line I'm sure but it's not a burning need at the moment.

@@ -0,0 +1,17 @@
package donotshipme
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why this is here or what it's supposed to do or prevent. Would you mind enlightening me? The panic still doesn't shed much light on it for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

HAH! Thanks for catching! Seems I already dropped the import of this so it shouldn't bite, but obviously forgot to clean this up.

This was from when it started out as a proof of concept. I was paranoid about this bleeding into our codebase somehow.

@Naatan Naatan dismissed stale reviews from samueld-activator and mitchell-as via 8ffadb2 July 16, 2025 17:43
@Naatan Naatan merged commit 169471b into master Jul 16, 2025
7 of 8 checks passed
@Naatan Naatan deleted the mcp branch July 16, 2025 17:54
mitchell-as added a commit that referenced this pull request Jul 17, 2025
This reverts commit 169471b, reversing
changes made to 897a3a9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants