diff --git a/airbyte/mcp/_datadog.py b/airbyte/mcp/_datadog.py index 0a30b735d..a08ff38b8 100644 --- a/airbyte/mcp/_datadog.py +++ b/airbyte/mcp/_datadog.py @@ -64,14 +64,20 @@ MCP_SESSION_ID_HEADER = "mcp-session-id" _MAX_INTENT_LENGTH = 4096 INTENT_INSTRUCTIONS_SENTENCE = ( - " Tools may accept an optional `intent` string; if present, " - "state in one sentence why you are calling the tool " - "(never credentials, identifiers or data values)." + " Tools may accept an optional `intent` string. This is internal metadata, " + "not a user-facing reply. Always write `intent` in English. " + "Describe the wider task and why you chose this tool in one English sentence, " + "regardless of the conversation's language. " + "Continue responding to the user in their preferred language. " + "Omit argument values, personal information, and secrets from `intent`." ) _INTENT_SCHEMA = { "type": "string", "description": ( - "Briefly describe the wider task and why you chose this tool, in English. " + "Internal metadata, not a user-facing reply. Always write `intent` in English. " + "Describe the wider task and why you chose this tool in one English sentence, " + "regardless of the conversation's language. " + "Continue responding to the user in their preferred language. " "Omit argument values, personal information, and secrets." ), } diff --git a/airbyte/mcp/cloud.py b/airbyte/mcp/cloud.py index 8e7336e5d..05124c5f2 100644 --- a/airbyte/mcp/cloud.py +++ b/airbyte/mcp/cloud.py @@ -1654,7 +1654,14 @@ def execute_external_api_query( # noqa: PLR0913 # Explicit args mirror the con intent: Annotated[ str | None, Field( - description="Optional free-text intent recorded with the request.", + description=( + "Optional intent recorded with the request. " + "Internal metadata, not a user-facing reply. Always write `intent` in English. " + "Describe the wider task and why you chose this tool in one English sentence, " + "regardless of the conversation's language. " + "Continue responding to the user in their preferred language. " + "Omit argument values, personal information, and secrets." + ), default=None, ), TraceArg.OMIT, @@ -1750,7 +1757,14 @@ def _execute_external_api_action( # noqa: PLR0913 # Explicit args mirror the c intent: Annotated[ str | None, Field( - description="Optional free-text intent recorded with the request.", + description=( + "Optional intent recorded with the request. " + "Internal metadata, not a user-facing reply. Always write `intent` in English. " + "Describe the wider task and why you chose this tool in one English sentence, " + "regardless of the conversation's language. " + "Continue responding to the user in their preferred language. " + "Omit argument values, personal information, and secrets." + ), default=None, ), ] = None, diff --git a/tests/unit_tests/test_mcp_cloud.py b/tests/unit_tests/test_mcp_cloud.py index a44f8f845..45536142f 100644 --- a/tests/unit_tests/test_mcp_cloud.py +++ b/tests/unit_tests/test_mcp_cloud.py @@ -54,7 +54,7 @@ AirbyteMissingResourceError, AirbyteLibInputError, ) -from fastmcp import Context +from fastmcp import Context, FastMCP @dataclass @@ -1866,6 +1866,33 @@ def test_execute_external_api_action_uses_write_path( assert kwargs["api_args"] == {"title": "Bug"} +@pytest.mark.parametrize( + "tool_fn", + [cloud_mcp.execute_external_api_query, cloud_mcp._execute_external_api_action], +) +def test_external_api_intent_guidance(tool_fn: Callable[..., Any]) -> None: + """Explicit intents request English metadata, not English user-facing replies.""" + app = FastMCP("intent-guidance-test") + app.tool(tool_fn) + (tool,) = asyncio.run(app.list_tools()) + schema = tool.parameters + intent = schema["properties"]["intent"] + + assert "Always write `intent` in English" in intent["description"] + assert "one English sentence" in intent["description"] + assert "regardless of the conversation's language" in intent["description"] + assert ( + "Continue responding to the user in their preferred language" + in intent["description"] + ) + assert ( + "Omit argument values, personal information, and secrets" + in intent["description"] + ) + assert "intent" not in schema.get("required", []) + assert intent["default"] is None + + def test_execute_external_api_action_is_not_advertised() -> None: """The write tool stays hidden until the backend supports write actions.""" from airbyte.mcp import server diff --git a/tests/unit_tests/test_mcp_datadog.py b/tests/unit_tests/test_mcp_datadog.py index d4dd4206f..98baa06b0 100644 --- a/tests/unit_tests/test_mcp_datadog.py +++ b/tests/unit_tests/test_mcp_datadog.py @@ -1360,6 +1360,34 @@ async def run(): ) assert response.status_code == 200 responses.append((method, params, response.json())) + instructions = responses[0][2]["result"]["instructions"] + assert "optional `intent`" in instructions + assert "Always write `intent` in English" in instructions + assert "one English sentence" in instructions + assert "regardless of the conversation's language" in instructions + assert ( + "Continue responding to the user in their preferred language" + in instructions + ) + assert ( + "Omit argument values, personal information, and secrets" + in instructions + ) + for tool in responses[1][2]["result"]["tools"]: + schema = tool["inputSchema"] + description = schema["properties"]["intent"]["description"] + assert "Always write `intent` in English" in description + assert "one English sentence" in description + assert "regardless of the conversation's language" in description + assert ( + "Continue responding to the user in their preferred language" + in description + ) + assert ( + "Omit argument values, personal information, and secrets" + in description + ) + assert "intent" not in schema.get("required", []) lifecycle_spans = [ span for span in spans