# KarmaDue with Microsoft Agent Framework

Tested: 2026-11-12 with agent-framework 1.25.0 on Python 3.12, using `MCPStreamableHTTPTool` against KarmaDue's MCP endpoint. No model or key was needed for this test. Real output:

    tools: ['check_install', 'search_recipes']
    check_install: {"ok": true, "v": "warn", "enforce": "ask", "why": "Caution before you install requests==2.31.0: 3 published advisories affect requests@2.31.0.", ...

`allowed_tools` keeps only the read tools. `ask` appears once it ships.

## File: `kd_agent_framework.py`

```python
"""KarmaDue in Microsoft Agent Framework: MCP tool with approval on check results, plus allowed_tools."""
import asyncio, os
from agent_framework import MCPStreamableHTTPTool

KD_MCP = "https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/mcp"
VIA = os.environ.get("KD_VIA", "recipe-agent-framework")

async def main():
    kd = MCPStreamableHTTPTool(
        name="karmadue",
        url=KD_MCP,
        static_headers={"x-kd-via": VIA},
        allowed_tools=["check_install", "search_recipes", "ask"],  # read-only tools
        approval_mode="never_require",                             # KarmaDue reads never need approval
        description="Ask KarmaDue before installing or researching: safety verdicts and tested fixes.",
    )
    async with kd:
        print("tools:", [f.name for f in kd.functions])
        fn = next(f for f in kd.functions if f.name == "check_install")
        out = await fn.invoke(target="requests==2.31.0", ecosystem="pypi")
        print("check_install:", " ".join(getattr(c, "text", None) or str(getattr(c, "value", c)) for c in (out if isinstance(out, list) else [out]))[:300])
        # Pass `kd` in tools=[...] of any ChatAgent, e.g. ChatAgent(chat_client=..., tools=[kd], instructions=...)

asyncio.run(main())
```

Note on result format: Agent Framework's default `tool_result_content="structured_first"` reads `structuredContent` when it is present. With KarmaDue's [compact mode](https://karmadue.expo.app/docs/ask.md#compact-mode) (`?compact=1`), it falls back to the text content, which carries the same answer.
