using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI; using System.ClientModel; // MCP Apps agent. // // This agent has no bespoke tools — the CopilotKit runtime is wired with // `mcpApps: { servers: [...] }` (see `src/app/api/copilotkit-mcp-apps/route.ts`) // pointing at a public MCP server (default: Excalidraw). The runtime // auto-applies the MCP Apps middleware which exposes the remote MCP // server's tools to this agent at request time and emits the activity // events that CopilotKit's built-in `MCPAppsActivityRenderer` renders in // the chat as a sandboxed iframe. // // Reference parity with: // showcase/integrations/langgraph-python/src/agents/mcp_apps_agent.py public sealed class McpAppsAgentFactory { private const string DefaultOpenAiEndpoint = "https://models.inference.ai.azure.com"; private const string SystemPrompt = "You draw simple diagrams in Excalidraw via the MCP tool.\n\n" + "SPEED MATTERS. Produce a correct-enough diagram fast; do not optimize " + "for polish. Target: one tool call, done in seconds.\n\n" + "When the user asks for a diagram:\n" + "1. Call `create_view` ONCE with 3-5 elements total: shapes + arrows + " + " an optional title text.\n" + "2. Use straightforward shapes (rectangle, ellipse, diamond) with plain " + " `label` fields (`{\"text\": \"...\", \"fontSize\": 18}`) on them.\n" + "3. Connect with arrows. Endpoints can be element centers or simple " + " coordinates — you don't need edge anchors / fixedPoint bindings.\n" + "4. Include ONE `cameraUpdate` at the END of the elements array that " + " frames the whole diagram. Use an approved 4:3 size (600x450 or " + " 800x600). No opening camera needed.\n" + "5. Reply with ONE short sentence describing what you drew.\n\n" + "Every element needs a unique string `id` (e.g. \"b1\", \"a1\", \"title\"). " + "Standard sizes: rectangles 160x70, ellipses/diamonds 120x80, 40-80px " + "gap between shapes.\n\n" + "Do NOT:\n" + "- Call `read_me`. You already know the basic shape API.\n" + "- Make multiple `create_view` calls.\n" + "- Iterate or refine. Ship on the first shot.\n" + "- Add decorative colors / fills / zone backgrounds unless the user " + " explicitly asks for them.\n" + "- Add labels on arrows unless crucial.\n\n" + "If the user asks for something specific (colors, more elements, " + "particular layout), follow their lead — but still in ONE call."; private readonly OpenAIClient _openAiClient; private readonly ILogger _logger; public McpAppsAgentFactory(IConfiguration configuration, ILoggerFactory loggerFactory) { ArgumentNullException.ThrowIfNull(configuration); ArgumentNullException.ThrowIfNull(loggerFactory); _logger = loggerFactory.CreateLogger(); var githubToken = configuration["GitHubToken"] ?? throw new InvalidOperationException( "GitHubToken not found in configuration. " + "Please set it using: dotnet user-secrets set GitHubToken \"\" " + "or get it using: gh auth token"); var endpointEnv = Environment.GetEnvironmentVariable("OPENAI_BASE_URL"); var endpoint = endpointEnv ?? DefaultOpenAiEndpoint; _openAiClient = new( new ApiKeyCredential(githubToken), AimockHeaderPolicy.CreateOpenAIClientOptions(endpoint)); } public AIAgent CreateMcpAppsAgent() { // gpt-4o-mini for speed — Excalidraw element emission is simple JSON // and we bias hard toward sub-30s generation. var chatClient = _openAiClient.GetChatClient("gpt-4o-mini").AsIChatClient(); return new ChatClientAgent( chatClient, name: "McpAppsAgent", description: SystemPrompt, tools: []); } }