Semantic Kernel with Caddey

Integrate Caddey tools into a Semantic Kernel agent using MCP and OAuth 2.0 Device Code flow.


Caddey exposes app features as tools, and Semantic Kernel agents can dynamically discover and invoke these tools via the Model Context Protocol (MCP). This guide demonstrates how to build a Python-based Semantic Kernel agent that connects to Caddey’s MCP endpoint, authenticates users, and executes tools seamlessly.

This example uses the OAuth 2.0 Device Code Flow, ideal for CLI-based agents and environments where browser redirects aren’t feasible. If your agent runs inside a web app or UI, consider using the Authorization Code Flow instead.

Try it in Google Colab! Want to run this integration interactively? Check out our Jupyter notebook that you can run directly in Google Colab.

Open In Colab

Prerequisites

  • Caddey OAuth Client ID (create a Public OAuth client in Caddey)
  • Python 3.x and a virtual environment
  • Semantic Kernel, Requests, and python-dotenv
pip install semantic-kernel[mcp] requests python-dotenv

Important: The OAuth 2.0 Device Code Flow (RFC 8628) only works with Public clients. When creating your OAuth client, you must select Public (not Confidential).

Confidential clients will receive an unauthorized_client error when attempting to use Device Authorization Grant.

OAuth 2.0 Device Code Flow

The Device Code Flow allows users to authenticate on another device (like a phone or browser) while your CLI agent waits for authorization.
It’s perfect for headless or console-based Semantic Kernel agents.

Complete Integration Script

The script below performs three key tasks:

  1. Authenticates the user via the OAuth 2.0 Device Code Flow
  2. Connects to Caddey’s MCP endpoint
  3. Creates a Semantic Kernel agent that can invoke Caddey tools
import os, time, asyncio, requests
from semantic_kernel import Kernel
from semantic_kernel.connectors.mcp import MCPClient
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.agents import Agent

# --- Step 1: User login via OAuth Device Code flow ---
DEVICE_URL = "https://auth.caddey.ai/realms/caddey/protocol/openid-connect/auth/device"
TOKEN_URL = "https://auth.caddey.ai/realms/caddey/protocol/openid-connect/token"
CLIENT_ID = os.getenv("CADDEY_CLIENT_ID")

# Request device and user codes
device_response = requests.post(DEVICE_URL, data={"client_id": CLIENT_ID})
device = device_response.json()

print("To sign in, open:", device["verification_uri_complete"])
print("Or visit", device["verification_uri"], "and enter code:", device["user_code"])

# Poll for access token
while True:
    token_response = requests.post(
        TOKEN_URL,
        data={
            "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
            "device_code": device["device_code"],
            "client_id": CLIENT_ID,
        },
    )
    token_data = token_response.json()

    if "access_token" in token_data:
        CADDEY_TOKEN = token_data["access_token"]
        break
    elif token_data.get("error") == "authorization_pending":
        time.sleep(device.get("interval", 5))
    else:
        raise Exception(f"Token error: {token_data}")

print("✅ Logged in. Token acquired.")

# --- Step 2: Connect Semantic Kernel to Caddey MCP ---
async def main():
    kernel = Kernel()

    # Create an MCP client to connect to Caddey’s MCP endpoint
    mcp_client = MCPClient(
        server_url="https://api.caddey.ai/mcp",
        transport="streamable_http",
        headers={"Authorization": f"Bearer {CADDEY_TOKEN}"},
    )

    # Register Caddey MCP tools as kernel functions
    await mcp_client.connect()
    caddey_tools = await mcp_client.get_tools()
    for tool in caddey_tools:
        kernel.add_function(tool.as_kernel_function())

    # Add an OpenAI LLM
    llm = OpenAIChatCompletion(model="gpt-4o-mini", temperature=0)
    kernel.add_text_completion_service("openai", llm)

    # Create a simple Semantic Kernel agent
    agent = Agent(kernel=kernel, name="CaddeyAgent")

    # Example interaction
    result = await agent.complete_chat("List my available tools.")
    print(result)

    await mcp_client.disconnect()

asyncio.run(main())

Using Your Agent

Once authenticated and connected, you can interact with your Semantic Kernel agent using natural language prompts.
The agent automatically discovers and invokes the appropriate Caddey tools through MCP.

Examples:

  • List all available tools in my Caddey account.
  • Create a new calendar event tomorrow at 10 AM titled “Team Sync.”
  • Send a message to Alice with the note “The report is ready.”

The Semantic Kernel handles tool discovery, schema mapping, and invocation automatically via MCP.

Important Notes

  • The Device Code Flow (RFC 8628) is designed for public clients and works seamlessly in CLI or server environments without requiring a web server for OAuth callbacks.
  • Public clients do not require a client secret, making this flow ideal for devices that cannot securely store credentials.
  • For web-based agents that can handle redirects, use the Authorization Code Flow instead.
  • Access tokens typically expire after 1 hour. Implement token refresh logic or re-run authentication when tokens expire.
  • Store your CADDEY_CLIENT_ID securely in environment variables.

Additional Resources

Need help? Visit the Support Section.