Add live web search to a Semantic Kernel agent
Semantic Kernel attaches any tool to its Kernel as a plugin — MCPStdioPlugin loads all five Superhighway tools at once over MCP, or a class of @kernel_function-decorated methods wraps the REST API for controlled spend. Either way a ChatCompletionAgent calls them automatically.
Two paths
| Path | Best for | What you need |
|---|---|---|
| MCPStdioPlugin | All five tools, fully autonomous — agent pays per call via x402 | A funded Base wallet + npx |
| @kernel_function + API key | Explicit native functions, controlled spend | A free API key from /pricing |
Path 1: MCP via MCPStdioPlugin (recommended for autonomous agents)
pip install "semantic-kernel[mcp]"
import asyncio, os
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.mcp import MCPStdioPlugin
from semantic_kernel.agents import ChatCompletionAgent
async def main():
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(
ai_model_id="gpt-4o",
api_key=os.environ["OPENAI_API_KEY"],
))
# Connect to the Superhighway MCP server and load all its tools.
# The async context manager opens the stdio connection and closes it on exit.
async with MCPStdioPlugin(
name="superhighway",
description="Live web search, news, images, scrape and research",
command="npx",
args=["-y", "superhighway-mcp"],
env={
"AGENT_PRIVATE_KEY": os.environ["AGENT_PRIVATE_KEY"],
"X402_NETWORK": "base",
},
) as superhighway:
kernel.add_plugin(superhighway)
agent = ChatCompletionAgent(
kernel=kernel,
name="search_agent",
instructions="You are a research assistant with access to live web search.",
)
response = await agent.get_response(
messages="What are the latest developments in AI agent frameworks?",
)
print(response)
asyncio.run(main())
AGENT_PRIVATE_KEY is the private key of a wallet with USDC on Base. Each call costs $0.001 and settles automatically via the x402 protocol — no signup, no API key, no human in the loop. MCPStdioPlugin connects to the server over stdio and registers one kernel function per tool, giving the agent all five: web_search, news_search, image_search, scrape, and research. Keep the agent work inside the async with block so the connection stays open while it runs.
Path 2: Native plugin with an API key
A Semantic Kernel plugin is just a class with @kernel_function-decorated methods. The name and description arguments are what the model sees when deciding whether to call a tool — annotate parameters with Annotated[...] so SK can build the schema.
pip install semantic-kernel requests
import asyncio, os
from typing import Annotated
import requests as _requests
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions import kernel_function
from semantic_kernel.agents import ChatCompletionAgent
BASE = "https://superhighway.walls.sh"
HEADERS = {"X-Api-Key": os.environ["SUPERHIGHWAY_API_KEY"]}
class SuperhighwayPlugin:
@kernel_function(name="web_search", description="Search the live web for current information and return ranked results.")
async def web_search(
self,
query: Annotated[str, "The search query."],
limit: Annotated[int, "Number of results to return."] = 5,
) -> str:
r = _requests.get(f"{BASE}/search", params={"q": query, "limit": limit}, headers=HEADERS)
return r.text
@kernel_function(name="news_search", description="Search for recent news articles with published dates.")
async def news_search(
self,
query: Annotated[str, "The search query."],
limit: Annotated[int, "Number of results to return."] = 5,
) -> str:
r = _requests.get(f"{BASE}/news", params={"q": query, "limit": limit}, headers=HEADERS)
return r.text
@kernel_function(name="research", description="Search the web and read top pages for deep synthesis.")
async def research(
self,
query: Annotated[str, "The research question."],
pages: Annotated[int, "How many top pages to read."] = 3,
) -> str:
r = _requests.get(f"{BASE}/research", params={"q": query, "pages": pages}, headers=HEADERS)
return r.text
async def main():
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(
ai_model_id="gpt-4o",
api_key=os.environ["OPENAI_API_KEY"],
))
kernel.add_plugin(SuperhighwayPlugin(), plugin_name="superhighway")
agent = ChatCompletionAgent(
kernel=kernel,
name="search_agent",
instructions="You are a research assistant with access to live web search.",
)
response = await agent.get_response(messages="What changed in the latest Python release?")
print(response)
asyncio.run(main())
The key Semantic Kernel pattern: a plugin is a plain class, and @kernel_function(name=..., description=...) is what the model reads — not the docstring. Add the instance with kernel.add_plugin(SuperhighwayPlugin(), plugin_name="superhighway"); the ChatCompletionAgent bound to that kernel automatically calls the functions and feeds the results back to the model. Use async for response in agent.invoke(messages=...) instead of get_response if you want to stream intermediate steps.
Which tools are available
| Tool / endpoint | What it returns | Price |
|---|---|---|
web_search / /search | Ranked web results — title, URL, description | $0.001 |
news_search / /news | Recent news with published dates | $0.001 |
image_search / /images | Image URLs, thumbnails, source pages | $0.001 |
scrape / /scrape | Any URL → clean markdown text | $0.002 |
research / /research | Search + read top pages in one call | $0.005 |
Full API reference: /openapi.json. Get a free API key or learn the x402 wallet flow.