Add live web search to a Semantic Kernel agent

Superhighway guides

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

PathBest forWhat you need
MCPStdioPluginAll five tools, fully autonomous — agent pays per call via x402A funded Base wallet + npx
@kernel_function + API keyExplicit native functions, controlled spendA 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 / endpointWhat it returnsPrice
web_search / /searchRanked web results — title, URL, description$0.001
news_search / /newsRecent news with published dates$0.001
image_search / /imagesImage URLs, thumbnails, source pages$0.001
scrape / /scrapeAny URL → clean markdown text$0.002
research / /researchSearch + read top pages in one call$0.005

Full API reference: /openapi.json. Get a free API key or learn the x402 wallet flow.