c5e0e5cfe3
Agents can now use Model Context Protocol servers. End to end: - SharedKernel seam IMcpGateway (ListToolsAsync / CallToolAsync) + McpToolDescriptor / McpToolResult, so the Assembler discovers and can invoke MCP tools without referencing Integrations' tables. - Integrations: McpServerConfig (org-scoped, owner-only; auth headers AES-GCM encrypted, never returned — only their names) + AddMcpServers migration. McpClient: a dependency-free Streamable-HTTP JSON-RPC 2.0 client (initialize → notifications/initialized → tools/list / tools/call), carrying the Mcp-Session-Id and parsing both application/json and text/event-stream replies. McpGateway resolves an org's servers, decrypts headers server-side, and is best-effort: an unreachable server is logged and skipped, never failing the run. CRUD + connectivity-test endpoints (create/test/delete owner-only via ManageApiKeys; list via ConfigureAgents to bind). - OrgBoard: Agent gains McpServerIds (uuid[]; migration backfills existing agents to empty) flowing through ConfigureAgent + AgentRunContext. - Assembler: AgentRunExecutor lists the agent's MCP tools (best-effort) and PromptAssembler renders a "# Tools (MCP)" catalog — labelled as data, never instructions — and records it in the run trace. - Client: SeatsPage gains an MCP servers card (add/test/delete, encrypted auth header) and a per-agent MCP server multi-select; api client gains del(). Note: discovery + the governed call gateway are in place now; the autonomous model-driven tool-call loop (model emits tool_calls → gated execution → feedback) needs a tool-calling model client and is the next increment — the stub model can't drive it. Verified: ArchitectureTests 8/8, IntegrationTests 53/53 (McpClientTests: JSON-RPC handshake/session, json + SSE; McpServerRegistryTests: owner-only, encrypted-header-never-returned, graceful test, Member 403; PromptAssemblerMcpTests: catalog + trace, omitted when empty), client build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.9 KiB
C#
63 lines
2.9 KiB
C#
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using TeamUp.Modules.Integrations.Ai;
|
|
using TeamUp.Modules.Integrations.Endpoints;
|
|
using TeamUp.Modules.Integrations.Git;
|
|
using TeamUp.Modules.Integrations.Persistence;
|
|
using TeamUp.Modules.Integrations.Security;
|
|
using TeamUp.SharedKernel.Ai;
|
|
using TeamUp.SharedKernel.Git;
|
|
using TeamUp.SharedKernel.Modularity;
|
|
using TeamUp.SharedKernel.Persistence;
|
|
|
|
namespace TeamUp.Modules.Integrations;
|
|
|
|
/// <summary>
|
|
/// BYOK API configs (encrypted, owner-only), the model-client adapters, and the Git connection.
|
|
/// Encryption keys are owner-only and server-side; the decrypted key never leaves the server.
|
|
/// </summary>
|
|
public sealed class IntegrationsModule : IModule
|
|
{
|
|
public string Name => "integrations";
|
|
|
|
public void Register(IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
var connectionString = configuration.GetConnectionString("Postgres")
|
|
?? throw new InvalidOperationException("Missing connection string 'ConnectionStrings:Postgres'.");
|
|
|
|
// BYOK credential store + encryption.
|
|
services.AddDbContext<IntegrationsDbContext>(options => options.UseNpgsql(connectionString));
|
|
services.AddScoped<IModuleDbContext>(sp => sp.GetRequiredService<IntegrationsDbContext>());
|
|
services.Configure<EncryptionOptions>(configuration.GetSection(EncryptionOptions.SectionName));
|
|
services.AddSingleton<ISecretProtector, AesGcmSecretProtector>();
|
|
services.AddScoped<IApiConfigResolver, ApiConfigResolver>();
|
|
services.TryAddSingleton(TimeProvider.System);
|
|
|
|
// Model clients: a router over per-provider adapters.
|
|
services.AddSingleton<StubModelClient>();
|
|
services.AddHttpClient<OpenAiCompatibleModelClient>();
|
|
services.AddScoped<IModelClient, ModelClientRouter>();
|
|
|
|
// MCP: a JSON-RPC client over HTTP + the org-scoped gateway agents talk through.
|
|
services.AddHttpClient<Mcp.McpClient>();
|
|
services.AddScoped<IMcpGateway, Mcp.McpGateway>();
|
|
|
|
// Git source (M2) — filesystem for dogfood, Gitea over REST when configured.
|
|
services.Configure<GitSourceOptions>(configuration.GetSection(GitSourceOptions.SectionName));
|
|
var gitOptions = configuration.GetSection(GitSourceOptions.SectionName).Get<GitSourceOptions>() ?? new GitSourceOptions();
|
|
if (string.Equals(gitOptions.Provider, "gitea", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
services.AddHttpClient<IGitProvider, GiteaGitProvider>();
|
|
}
|
|
else
|
|
{
|
|
services.AddSingleton<IGitProvider, FileSystemGitProvider>();
|
|
}
|
|
}
|
|
|
|
public void MapEndpoints(IEndpointRouteBuilder endpoints) => IntegrationsEndpoints.Map(endpoints);
|
|
}
|