Skip to content

Commit c774a9a

Browse files
mjudeikisclaude
andauthored
fix(app-studio): unlock infra + repo MCP tools for instance/git turns (#370)
* fix(app-studio): load infra MCP tools for instance/platform turns The assistant only fetches the aggregated infrastructure MCP tools when the last user message matches a keyword gate (projectAssistantTurnNeedsInfrastructureMCP). The list covered template/provisioning vocabulary but omitted the instance lifecycle, so prompts like "list instances via mcp" never loaded infrastructure__list_instances and the model truthfully replied it had no instance list available. Add instance/instances, platform, and mcp to the keyword list and a test asserting the gate trips on instance/platform/mcp phrasing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(app-studio): classify git/PR turns as implementation Repo-mutating turns ("push my changes", "open a pull request", "merge the branch") fell through to the discussion profile, so the commit/repo MCP bridge was never unlocked for them. Add git, push, pull request, branch, and merge to the implementation keyword set (skipping a bare "pr", which is a substring of too many common words). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bf1c424 commit c774a9a

4 files changed

Lines changed: 33 additions & 1 deletion

File tree

providers/app-studio/api/assistant_eino_tool.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ func projectAssistantTurnNeedsInfrastructureMCP(history []store.Message) bool {
135135
}
136136
content := strings.ToLower(strings.TrimSpace(history[i].Content))
137137
return containsProjectAssistantTurnKeyword(content, []string{
138-
"infrastructure", "infra", "template", "templates", "provision", "database", "postgres", "redis", "supporting resource", "provider",
138+
"infrastructure", "infra", "template", "templates", "provision",
139+
"instance", "instances", "database", "postgres", "redis",
140+
"supporting resource", "provider", "platform", "mcp",
139141
})
140142
}
141143
return false

providers/app-studio/api/assistant_eino_tool_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,35 @@ import (
2222
"testing"
2323

2424
aiv1alpha1 "github.com/faroshq/provider-app-studio/apis/ai/v1alpha1"
25+
"github.com/faroshq/provider-app-studio/store"
2526
"github.com/faroshq/provider-app-studio/workspace"
2627
)
2728

29+
func TestProjectAssistantTurnNeedsInfrastructureMCP(t *testing.T) {
30+
for _, tc := range []struct {
31+
name string
32+
content string
33+
want bool
34+
}{
35+
{"list instances", "list instances via mcp", true},
36+
{"single instance", "show me the status of my instance", true},
37+
{"platform vocabulary", "what platform resources do I have?", true},
38+
{"mcp mention", "call mcp to enumerate things", true},
39+
{"templates", "what templates are available?", true},
40+
{"unrelated", "fix the button styling in app.js", false},
41+
} {
42+
t.Run(tc.name, func(t *testing.T) {
43+
history := []store.Message{{
44+
Role: aiv1alpha1.ProjectMessageRoleUser,
45+
Content: tc.content,
46+
}}
47+
if got := projectAssistantTurnNeedsInfrastructureMCP(history); got != tc.want {
48+
t.Fatalf("projectAssistantTurnNeedsInfrastructureMCP(%q) = %v, want %v", tc.content, got, tc.want)
49+
}
50+
})
51+
}
52+
}
53+
2854
func TestEinoApprovePlanToolRejectsMissingAllowedOperations(t *testing.T) {
2955
runState := newProjectEinoAssistantRunState()
3056
tool := projectEinoAssistantTool{runState: runState}

providers/app-studio/api/assistant_turn_profile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func fallbackProjectAssistantTurnDecisionForMessage(content string) projectAssis
166166
}
167167
if containsProjectAssistantTurnKeyword(normalized, []string{
168168
"build", "add", "change", "update", "implement", "write", "make the app", "create", "remove", "delete", "ship", "commit", "deploy", "provision",
169+
"git", "push", "pull request", "branch", "merge",
169170
}) {
170171
return fallbackProjectAssistantTurnDecisionWithProfile(projectAssistantTurnProfileImplementation)
171172
}

providers/app-studio/api/assistant_turn_profile_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func TestProjectAssistantTurnProfileClassifier(t *testing.T) {
4343
{name: "debug fix", message: "Fix the failed fetch error and make it work", want: projectAssistantTurnProfileDebugFix},
4444
{name: "fix only fallback", message: "Please fix the login form", want: projectAssistantTurnProfileDebugFix},
4545
{name: "implementation", message: "Add a search field to the todo app", want: projectAssistantTurnProfileImplementation},
46+
{name: "git push", message: "push my changes to git", want: projectAssistantTurnProfileImplementation},
47+
{name: "pull request", message: "open a pull request for this branch", want: projectAssistantTurnProfileImplementation},
48+
{name: "merge", message: "merge the feature branch", want: projectAssistantTurnProfileImplementation},
4649
}
4750
for _, tt := range tests {
4851
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)