Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions integrations/catalog-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import entry74 from "./catalog/resend.json" with { type: "json" };
import entry75 from "./catalog/sequential-thinking.json" with { type: "json" };
import entry76 from "./catalog/superhuman-mail.json" with { type: "json" };
import entry77 from "./catalog/time.json" with { type: "json" };
import entry78 from "./catalog/youcom-search.json" with { type: "json" };

export const INTEGRATION_CATALOG_ENTRIES = [
entry0,
Expand Down Expand Up @@ -160,4 +161,5 @@ export const INTEGRATION_CATALOG_ENTRIES = [
entry75,
entry76,
entry77,
entry78,
];
26 changes: 26 additions & 0 deletions integrations/catalog/youcom-search.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"id": "youcom-search",
"name": "You.com",
"description": "Web search with cited results via the You.com MCP server. Free tier works without an API key.",
"docsUrl": "https://you.com/docs",
"keywords": [
"search",
"web",
"research",
"you",
"citations"
],
"connectionOptions": [
{
"id": "none",
"provider": "mcp",
"transport": {
"kind": "shttp",
"url": "https://api.you.com/mcp?profile=free"
},
"auth": {
"strategy": "none"
}
}
]
}
2 changes: 1 addition & 1 deletion tests/test_catalogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_catalog_entries_have_required_fields():


def test_remote_no_auth_mcp_entries_are_intentionally_public():
public_remote_mcp_ids = {"cloudflare-docs", "deepwiki", "huggingface"}
public_remote_mcp_ids = {"cloudflare-docs", "deepwiki", "huggingface", "youcom-search"}

actual = set()
for entry in load_catalog_entries("integrations/catalog"):
Expand Down
41 changes: 41 additions & 0 deletions tests/test_live_integration_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@

Evidence lines are printed as ``[OSS-5193] ...`` and contain only tool names,
counts, and provider error codes — never credentials or message content.

The anonymous You.com search smoke test needs no credentials and runs only
when explicitly enabled:

RUN_YOUCOM_MCP_LIVE=1 uv run pytest tests/test_live_integration_smoke.py -k youcom -s
"""

import json
Expand Down Expand Up @@ -364,3 +369,39 @@ def test_slack_invalid_token_yields_in_band_error():
f"slack: invalid token -> in-band ok=false error={payload.get('error')} "
f"(is_error={is_error})"
)


@pytest.mark.skipif(
os.environ.get("RUN_YOUCOM_MCP_LIVE") != "1",
reason="set RUN_YOUCOM_MCP_LIVE=1 to run the anonymous You.com MCP smoke test",
)
def test_youcom_anonymous_search():
option = _installable_option("youcom-search")
assert option.auth.strategy == "none"
assert option.transport.kind == "shttp"
server = MCPServer.model_validate(
{"url": option.transport.url, "transport": "http"}
)

with create_mcp_tools({"youcom-search": server}, timeout=HTTP_TIMEOUT) as client:
assert "you-search" in {tool.name for tool in client.tools}
result = client.call_async_from_sync(
client.call_tool_mcp,
name="you-search",
arguments={
"query": "OpenHands AI agent platform GitHub repository",
"count": 5,
},
timeout=HTTP_TIMEOUT,
)
assert not result.is_error

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

client.call_tool_mcp(...) returns an mcp.types.CallToolResult (via MCPClient/fastmcp), whose flag is isError - there is no is_error attribute. With the repo's pinned mcp==1.30.0 and fastmcp==3.4.7, running the documented RUN_YOUCOM_MCP_LIVE=1 ... pytest tests/test_live_integration_smoke.py -k youcom -s on this head fails here with AttributeError: 'CallToolResult' object has no attribute 'is_error'. Did you mean: 'isError'?, so none of the payload assertions below are reached.

Use result.isError, or better, reuse the existing _connect helper in this file (line 132) which already normalizes the result and joins the TextContent blocks:

with _connect("youcom-search", server, HTTP_TIMEOUT) as (tool_names, call):
    assert "you-search" in tool_names
    is_error, text = call(
        "you-search",
        {"query": "OpenHands AI agent platform GitHub repository", "count": 5},
    )
    assert not is_error, f"you-search failed: {text}"

text = "\n".join(
block.text
for block in result.content
if isinstance(block, mcp.types.TextContent)
)
payload = json.loads(text)
results = payload["results"]["web"]
assert results, "you-search returned no web results"
assert all("url" in r for r in results)
_evidence(f"youcom: anonymous you-search -> {len(results)} web results")
Loading