mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(copilot): replace MCP jargon with user-friendly language (#12381)
Closes SECRT-2105 ### Changes 🏗️ Replace all user-facing MCP technical terminology with plain, friendly language across the CoPilot UI and LLM prompting. **Backend (`run_mcp_tool.py`)** - Added `_service_name()` helper that extracts a readable name from an MCP host (`mcp.sentry.dev` → `Sentry`) - `agent_name` in `SetupRequirementsResponse`: `"MCP: mcp.sentry.dev"` → `"Sentry"` - Auth message: `"The MCP server at X requires authentication. Please connect your credentials to continue."` → `"To continue, sign in to Sentry and approve access."` **Backend (`mcp_tool_guide.md`)** - Added "Communication style" section with before/after examples to teach the LLM to avoid "MCP server", "OAuth", "credentials" jargon in responses to users **Frontend (`MCPSetupCard.tsx`)** - Button: `"Connect to mcp.sentry.dev"` → `"Connect Sentry"` - Connected state: `"Connected to mcp.sentry.dev!"` → `"Connected to Sentry!"` - Retry message: `"I've connected the MCP server credentials. Please retry."` → `"I've connected. Please retry."` **Frontend (`helpers.tsx`)** - Added `serviceNameFromHost()` helper (exported, mirrors the backend logic) - Run text: `"Discovering MCP tools on mcp.sentry.dev"` → `"Connecting to Sentry…"` - Run text: `"Connecting to MCP server"` → `"Connecting…"` - Run text: `"Connect to MCP: mcp.sentry.dev"` → `"Connect Sentry"` (uses `agent_name` which is now just `"Sentry"`) - Run text: `"Discovered N tool(s) on mcp.sentry.dev"` → `"Connected to Sentry"` - Error text: `"MCP error"` → `"Connection error"` ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [ ] Open CoPilot and ask it to connect to a service (e.g. Sentry, Notion) - [ ] Verify the run text accordion title shows `"Connecting to Sentry…"` instead of `"Discovering MCP tools on mcp.sentry.dev"` - [ ] Verify the auth card button shows `"Connect Sentry"` instead of `"Connect to mcp.sentry.dev"` - [ ] Verify the connected state shows `"Connected to Sentry!"` instead of `"Connected to mcp.sentry.dev!"` - [ ] Verify the LLM response text avoids "MCP server", "OAuth", "credentials" terminology
This commit is contained in:
@@ -26,3 +26,17 @@ For other services, search the MCP registry at https://registry.modelcontextprot
|
||||
|
||||
If the server requires credentials, a `SetupRequirementsResponse` is returned with an OAuth
|
||||
login prompt. Once the user completes the flow and confirms, retry the same call immediately.
|
||||
|
||||
### Communication style
|
||||
|
||||
Avoid technical jargon like "MCP server", "OAuth", or "credentials" when talking to the user.
|
||||
Use plain, friendly language instead:
|
||||
|
||||
| Instead of… | Say… |
|
||||
|---|---|
|
||||
| "Let me connect to Sentry's MCP server and discover what tools are available." | "I can connect to Sentry and help identify important issues." |
|
||||
| "Let me connect to Sentry's MCP server now." | "Next, I'll connect to Sentry." |
|
||||
| "The MCP server at mcp.sentry.dev requires authentication. Please connect your credentials to continue." | "To continue, sign in to Sentry and approve access." |
|
||||
| "Sentry's MCP server needs OAuth authentication. You should see a prompt to connect your Sentry account…" | "You should see a prompt to sign in to Sentry. Once connected, I can help surface critical issues right away." |
|
||||
|
||||
Use **"connect to [Service]"** or **"sign in to [Service]"** — never "MCP server", "OAuth", or "credentials".
|
||||
|
||||
@@ -34,6 +34,11 @@ logger = logging.getLogger(__name__)
|
||||
_AUTH_STATUS_CODES = {401, 403}
|
||||
|
||||
|
||||
def _service_name(host: str) -> str:
|
||||
"""Strip the 'mcp.' prefix from an MCP hostname: 'mcp.sentry.dev' → 'sentry.dev'"""
|
||||
return host[4:] if host.startswith("mcp.") else host
|
||||
|
||||
|
||||
class RunMCPToolTool(BaseTool):
|
||||
"""
|
||||
Tool for discovering and executing tools on any MCP server.
|
||||
@@ -303,8 +308,8 @@ class RunMCPToolTool(BaseTool):
|
||||
)
|
||||
return ErrorResponse(
|
||||
message=(
|
||||
f"The MCP server at {server_host(server_url)} requires authentication, "
|
||||
"but no credential configuration was found."
|
||||
f"Unable to connect to {_service_name(server_host(server_url))} "
|
||||
"— no credentials configured."
|
||||
),
|
||||
session_id=session_id,
|
||||
)
|
||||
@@ -312,15 +317,13 @@ class RunMCPToolTool(BaseTool):
|
||||
missing_creds_list = list(missing_creds_dict.values())
|
||||
|
||||
host = server_host(server_url)
|
||||
service = _service_name(host)
|
||||
return SetupRequirementsResponse(
|
||||
message=(
|
||||
f"The MCP server at {host} requires authentication. "
|
||||
"Please connect your credentials to continue."
|
||||
),
|
||||
message=(f"To continue, sign in to {service} and approve access."),
|
||||
session_id=session_id,
|
||||
setup_info=SetupInfo(
|
||||
agent_id=server_url,
|
||||
agent_name=f"MCP: {host}",
|
||||
agent_name=service,
|
||||
user_readiness=UserReadiness(
|
||||
has_all_credentials=False,
|
||||
missing_credentials=missing_creds_dict,
|
||||
|
||||
@@ -756,4 +756,4 @@ async def test_build_setup_requirements_returns_setup_response():
|
||||
)
|
||||
assert isinstance(result, SetupRequirementsResponse)
|
||||
assert result.setup_info.agent_id == _SERVER_URL
|
||||
assert "authentication" in result.message.lower()
|
||||
assert "sign in" in result.message.lower()
|
||||
|
||||
@@ -235,8 +235,8 @@ describe("getAnimationText", () => {
|
||||
state: "input-streaming",
|
||||
...BASE,
|
||||
});
|
||||
expect(text).toContain("Discovering");
|
||||
expect(text).toContain("mcp.example.com");
|
||||
expect(text).toContain("Connecting");
|
||||
expect(text).toContain("example.com");
|
||||
});
|
||||
|
||||
it("shows tool call text when tool_name is set", () => {
|
||||
@@ -245,7 +245,7 @@ describe("getAnimationText", () => {
|
||||
input: { server_url: "https://mcp.example.com/mcp", tool_name: "fetch" },
|
||||
});
|
||||
expect(text).toContain("fetch");
|
||||
expect(text).toContain("mcp.example.com");
|
||||
expect(text).toContain("example.com");
|
||||
});
|
||||
|
||||
it("includes query argument preview when tool_arguments has a query key", () => {
|
||||
@@ -282,7 +282,7 @@ describe("getAnimationText", () => {
|
||||
tool_arguments: {},
|
||||
},
|
||||
});
|
||||
expect(text).toBe("Calling list_users on mcp.example.com");
|
||||
expect(text).toBe("Calling list_users on example.com");
|
||||
});
|
||||
|
||||
it("truncates long argument previews to 60 chars with ellipsis", () => {
|
||||
@@ -327,8 +327,8 @@ describe("getAnimationText", () => {
|
||||
output: DISCOVERY,
|
||||
input: { server_url: "https://mcp.example.com/mcp" },
|
||||
});
|
||||
expect(text).toContain("Discovered");
|
||||
expect(text).toContain("1");
|
||||
expect(text).toContain("Connected");
|
||||
expect(text).toContain("example.com");
|
||||
});
|
||||
|
||||
it("shows setup label on output-available for setup requirements", () => {
|
||||
|
||||
@@ -12,8 +12,6 @@ import { CredentialsProvidersContext } from "@/providers/agent-credentials/crede
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useCopilotChatActions } from "../../../../components/CopilotChatActionsProvider/useCopilotChatActions";
|
||||
import { ContentMessage } from "../../../../components/ToolAccordion/AccordionContent";
|
||||
import { serverHost } from "../../helpers";
|
||||
|
||||
interface Props {
|
||||
output: SetupRequirementsResponse;
|
||||
/**
|
||||
@@ -38,7 +36,8 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
|
||||
// setup_info.agent_id is set to the server_url in the backend
|
||||
const serverUrl = output.setup_info.agent_id;
|
||||
const host = serverHost(serverUrl);
|
||||
// agent_name is computed by the backend as the display name for the service
|
||||
const service = output.setup_info.agent_name;
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -95,10 +94,7 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
}
|
||||
|
||||
setConnected(true);
|
||||
onSend(
|
||||
retryInstruction ??
|
||||
"I've connected the MCP server credentials. Please retry.",
|
||||
);
|
||||
onSend(retryInstruction ?? "I've connected. Please retry.");
|
||||
} catch (e: unknown) {
|
||||
const err = e as Record<string, unknown>;
|
||||
if (err?.status === 400) {
|
||||
@@ -137,10 +133,7 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
if (!(res.status >= 200 && res.status < 300))
|
||||
throw new Error("Failed to store token");
|
||||
setConnected(true);
|
||||
onSend(
|
||||
retryInstruction ??
|
||||
"I've connected the MCP server credentials. Please retry.",
|
||||
);
|
||||
onSend(retryInstruction ?? "I've connected. Please retry.");
|
||||
} catch (e: unknown) {
|
||||
const err = e as Record<string, unknown>;
|
||||
setError(
|
||||
@@ -155,7 +148,7 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
if (connected) {
|
||||
return (
|
||||
<div className="mt-2 rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700">
|
||||
Connected to {host}!
|
||||
Connected to {service}!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -171,7 +164,7 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
onClick={handleConnect}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Connecting…" : `Connect to ${host}`}
|
||||
{loading ? "Connecting…" : `Connect ${service}`}
|
||||
</Button>
|
||||
|
||||
{error && (
|
||||
@@ -184,7 +177,7 @@ export function MCPSetupCard({ output, retryInstruction }: Props) {
|
||||
<div className="mt-3 flex gap-2">
|
||||
<input
|
||||
type="password"
|
||||
aria-label={`API token for ${host}`}
|
||||
aria-label={`API token for ${service}`}
|
||||
placeholder="Paste API token"
|
||||
value={manualToken}
|
||||
onChange={(e) => setManualToken(e.target.value)}
|
||||
|
||||
@@ -32,11 +32,11 @@ vi.mock("@/app/api/__generated__/endpoints/mcp/mcp", () => ({
|
||||
function makeSetupOutput(serverUrl = "https://mcp.example.com/mcp") {
|
||||
return {
|
||||
type: "setup_requirements" as const,
|
||||
message: "The MCP server at mcp.example.com requires authentication.",
|
||||
message: "To continue, sign in to example.com and approve access.",
|
||||
session_id: "test-session",
|
||||
setup_info: {
|
||||
agent_id: serverUrl,
|
||||
agent_name: "MCP: mcp.example.com",
|
||||
agent_name: "example.com",
|
||||
user_readiness: {
|
||||
has_all_credentials: false,
|
||||
missing_credentials: {},
|
||||
@@ -58,9 +58,9 @@ describe("MCPSetupCard", () => {
|
||||
|
||||
it("renders setup message and connect button", () => {
|
||||
render(<MCPSetupCard output={makeSetupOutput()} />);
|
||||
expect(screen.getByText(/requires authentication/)).toBeDefined();
|
||||
expect(screen.getByText(/sign in to example\.com/i)).toBeDefined();
|
||||
expect(
|
||||
screen.getByRole("button", { name: /connect to mcp.example.com/i }),
|
||||
screen.getByRole("button", { name: /connect example\.com/i }),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ describe("MCPSetupCard", () => {
|
||||
|
||||
render(<MCPSetupCard output={makeSetupOutput()} />);
|
||||
fireEvent.click(
|
||||
screen.getByRole("button", { name: /connect to mcp.example.com/i }),
|
||||
screen.getByRole("button", { name: /connect example\.com/i }),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -100,7 +100,7 @@ describe("MCPSetupCard", () => {
|
||||
|
||||
render(<MCPSetupCard output={makeSetupOutput()} />);
|
||||
fireEvent.click(
|
||||
screen.getByRole("button", { name: /connect to mcp.example.com/i }),
|
||||
screen.getByRole("button", { name: /connect example\.com/i }),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -127,7 +127,7 @@ describe("MCPSetupCard", () => {
|
||||
fireEvent.click(screen.getByRole("button", { name: /use token/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/connected to mcp.example.com/i)).toBeDefined();
|
||||
expect(screen.getByText(/connected to example\.com/i)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -125,6 +125,11 @@ export function serverHost(url: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
/** Strip the 'mcp.' prefix from an MCP hostname: 'mcp.sentry.dev' → 'sentry.dev' */
|
||||
export function serviceNameFromHost(host: string): string {
|
||||
return host.startsWith("mcp.") ? host.slice(4) : host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a short preview of the most meaningful argument value, e.g. `"my query"`.
|
||||
* Checks common "query" key names first, then falls back to the first string value.
|
||||
@@ -174,28 +179,30 @@ export function getAnimationText(part: {
|
||||
const host = input?.server_url ? serverHost(input.server_url) : "";
|
||||
const toolName = input?.tool_name?.trim();
|
||||
|
||||
const service = host ? serviceNameFromHost(host) : "";
|
||||
|
||||
switch (part.state) {
|
||||
case "input-streaming":
|
||||
case "input-available": {
|
||||
if (!toolName) return `Discovering MCP tools${host ? ` on ${host}` : ""}`;
|
||||
if (!toolName) return `Connecting to ${service || "integration"}…`;
|
||||
const argPreview = getArgPreview(input?.tool_arguments);
|
||||
return `Calling ${toolName}${argPreview ? `(${argPreview})` : ""}${host ? ` on ${host}` : ""}`;
|
||||
return `Calling ${toolName}${argPreview ? `(${argPreview})` : ""}${service ? ` on ${service}` : ""}`;
|
||||
}
|
||||
case "output-available": {
|
||||
const output = getRunMCPToolOutput(part);
|
||||
if (!output) return "Connecting to MCP server";
|
||||
if (!output) return "Connecting…";
|
||||
if (isSetupRequirementsOutput(output))
|
||||
return `Connect to ${output.setup_info.agent_name}`;
|
||||
return `Connect ${output.setup_info.agent_name}`;
|
||||
if (isMCPToolOutput(output))
|
||||
return `Ran ${output.tool_name}${host ? ` on ${host}` : ""}`;
|
||||
return `Ran ${output.tool_name}${service ? ` on ${service}` : ""}`;
|
||||
if (isDiscoveryOutput(output))
|
||||
return `Discovered ${output.tools.length} tool(s) on ${serverHost(output.server_url)}`;
|
||||
return "MCP error";
|
||||
return `Connected to ${serviceNameFromHost(serverHost(output.server_url))}`;
|
||||
return "Connection error";
|
||||
}
|
||||
case "output-error":
|
||||
return "MCP error";
|
||||
return "Connection error";
|
||||
default:
|
||||
return "Connecting to MCP server";
|
||||
return "Connecting…";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user