Skip to content
Menu

What's new in Beacon 5.0

View .md

Beacon 5.0 was released on 21 October 2026 (NuGet version 5.0.0). Its headline is the MCP gateway: one package that turns your own ASP.NET Core application into an MCP server, so Claude and other AI clients act as your signed-in users through your own endpoints. Around it come test and verification tooling, a Claude Code plugin, LLM providers as packages, and a lighter dependency graph.

Moberg.Beacon.Gateway embeds Beacon as the MCP server of an existing application. People connect Claude to https://your-app/beacon/mcp and sign in with Microsoft Entra. A tool call then runs one of your endpoints as that person, through your own authorization, and Beacon audits it. Your Markdown documents can ground the answers. There are no data sources and no SQL. Read-only enforcement is pinned, and so is no content retention unless you set RetainToolContent. /beacon/mcp answers 401/403 itself and never redirects to your sign-in.

builder.Services.AddBeaconMcpGateway(builder.Configuration, x =>
{
x.ProjectName = "app";
x.Database(db => db.UsePostgreSql());
x.Entra(builder.Configuration.GetSection("AzureAd"));
x.Users<AppMcpUsers>(); // your IMcpHostUserResolver: who a token is
});
// …
app.UseBeaconMcpGateway();
app.MapBeaconMcpGateway();

An optional admin UI serves the MCP audit, the settings and your documents at /beacon to holders of one of your own authorization policies, signed in through your own sign-in. → MCP gateway for your application

Endpoint tools, ready for real applications

Section titled “Endpoint tools, ready for real applications”
  • [BeaconTool] works on handler classes whose framework copies class attributes into the endpoint, such as a Moberg Warp handler. On an MVC controller class it fails at startup and tells you to put the attribute on the action.
  • BindFromCaller("userId", "app_user_id") fills the person’s own id from the caller and removes the argument from the tool’s schema. An agent cannot pass someone else’s id.
  • Tool calls run your IExceptionHandlers, so a refusal reaches the agent as your own problem details.
  • httpContext.User.IsBeaconMcpCall() tells you a request is an agent’s. You can then hold agents to less than the browser.
  • AddBeaconMcp(x => …) chooses the built-in tools, turns saved-query tools off and replaces the server instructions.

→ Host endpoint tools, recipe: Expose an endpoint as a tool

  • Moberg.Beacon.Testing has Entra-shaped test tokens, an in-memory user resolver and a real MCP client over your test server (McpTestTokens, McpTestUsers, McpTestClient). Your tests then go through the bearer middleware, the caller mapping, the MCP filters, the audit and your endpoint’s own authorization.
  • beacon-doctor (dotnet tool install -g Moberg.Beacon.Doctor) checks a deployed endpoint the way an MCP client sees it: the 401 challenge, the protected-resource metadata, the authorization server, PKCE and offline_access. With --token it also runs initialize and tools/list. It prints PASS/WARN/FAIL lines and exits non-zero on a failure.
  • src/Beacon.SampleGateway in the repository is a reference gateway host.
  • A Claude Code plugin ships in the repository: /plugin marketplace add moberghr/beacon, then /plugin install beacon@moberg-beacon. → Build with AI
  • Cookbooks: recipes that run start to finish, each with a prompt you can hand to your agent. → Cookbooks
  • New one-file bundles: llms-mcp.txt, llms-cookbooks.txt and llms-gateway.txt, next to llms-integrate.txt and llms-full.txt.

AddBeaconAI registers OpenAI, Azure OpenAI and Anthropic (Claude). AWS Bedrock moved to its own package, Moberg.Beacon.AI.Bedrock, which you activate with AddBeaconBedrock(), so Moberg.Beacon.AI no longer depends on the AWS SDK. The admin settings page lists only the providers you installed (GET /beacon/api/admin-settings/llm-providers). A configured provider that is not installed leaves AI without a provider: the host logs a warning, and AI calls fail with a message that names the package to add.

Local embeddings moved to their own package too: Moberg.Beacon.AI.Embeddings.Onnx, activated with AddBeaconOnnxEmbeddings(). Without it, retrieval is keyword-based and Moberg.Beacon.AI carries no ONNX runtime.

Migration writers in the connectors, and a lighter build

Section titled “Migration writers in the connectors, and a lighter build”

Data-migration writes moved out of Moberg.Beacon.Core into the destination’s connector, behind IMigrationBulkWriter. PostgreSQL writes with binary COPY, SQL Server with SqlBulkCopy and MERGE, MySQL with multi-row parameterised inserts, Upsert with ON DUPLICATE KEY UPDATE, and Truncate-Load. Every write, the clear and the primary key lookup run on the run’s one connection and transaction, so a run is all-or-nothing on PostgreSQL and SQL Server, and an Insert or Upsert run is all-or-nothing on MySQL InnoDB. The exception is a MySQL Truncate-Load: TRUNCATE commits on its own, so a run that fails after the clear leaves the table empty. EFCore.BulkExtensions and Pomelo.EntityFrameworkCore.MySql have left the build, and Moberg.Beacon.Core no longer brings the SQL Server EF Core provider with it.

  • Beacon’s admin standing comes from the stored user’s live roles, not from the session’s role claim (see the upgrade list). Demoting someone takes effect on their next request.
  • /beacon/mcp accepts MCP credentials only: an API key or a mapped bearer token.
  • Every SQL gate request now carries a row-level security context. This is groundwork: 5.0 filters no rows, and the lock Beacon:RowSecurity:Enabled stays off (a host that sets it on does not start).
PackageWhat it is
Moberg.Beacon.GatewayThe MCP gateway for your own application
Moberg.Beacon.TestingTest helpers for a gateway host’s MCP tools
Moberg.Beacon.DoctorThe beacon-doctor .NET tool
Moberg.Beacon.AI.BedrockThe AWS Bedrock LLM provider, previously inside Moberg.Beacon.AI
Moberg.Beacon.AI.Embeddings.OnnxLocal ONNX embeddings for semantic retrieval, previously inside Moberg.Beacon.AI

Upgrade every Moberg.Beacon.* package to 5.0.0 together, then go through this list. Most hosts need only the first two items.

  • Apply the new migration. AddRowSecurityModel adds five tables in the beacon schema and a column on data_sources, on PostgreSQL and on SQL Server. A host that calls Beacon.Core.ServiceConfiguration.UseBeacon(app.Services, createSchema: true) applies it at startup. A host that migrates in a separate step can call BeaconPostgreSqlMigrator.MigrateAsync(...) or BeaconSqlServerMigrator.MigrateAsync(...) from it. → Metadata database
  • Bedrock users: add the package. Reference Moberg.Beacon.AI.Bedrock and call services.AddBeaconBedrock() (before or after AddBeaconAI). Without them, Bedrock is not available and AI calls fail with “LLM provider Bedrock is not installed”. → AI providers
  • Semantic retrieval: add the embeddings package. Reference Moberg.Beacon.AI.Embeddings.Onnx and call services.AddBeaconOnnxEmbeddings() (before or after AddBeaconAI). AddBeaconEmbeddings() is obsolete and no longer loads the model. Without the package, retrieval falls back to keywords; with Beacon:Embeddings:Enabled on, the host logs one warning the first time retrieval asks for embeddings. → AI providers
  • Migration targets: register the connector. A data-migration run writes through the destination’s connector, so a PostgreSQL, SQL Server or MySQL target needs that connector package and its Add<Engine>Connector() call. A run into an engine without one fails before Beacon decrypts the target’s credentials. If you run MySQL Truncate-Load jobs, keep a way to reload the table: a failed run leaves it empty. → Data migration
  • Central package management: raise your pins. If restore fails with NU1109 (“Detected package downgrade”), raise each pinned package to the version the message names (EF Core and Microsoft.Extensions.* patches, ClosedXML). → Packages
  • No browser sessions on /beacon/mcp. A signed-in browser session, Beacon’s or yours, now gets 403 there. MCP clients use an API key or a mapped Entra token. The MCP playground is not affected: it calls /beacon/api. → Connect AI clients
  • Hosts that own sign-in: grant Admin in Beacon. While Beacon manages users, Admin comes from the Beacon user’s own roles. An identity-provider role named Admin (an Entra app role, for example) no longer makes someone Beacon’s Admin: grant the Admin role to their Beacon user. Functional permissions mapped with Beacon:Authorization:IdpRoles are unchanged. Hosts without Beacon user management keep the claim. → User management
  • Hosts that use Core without the API or MCP packages: the request principal must be registered. Call AddBeaconApiServices, AddBeaconCookieAuthentication or AddBeaconMcp, or register your own with services.UseRequestPrincipal<T>(). IHttpContextAccessor alone no longer identifies the caller. → Host wiring
  • SQL Server provider no longer transitive. Moberg.Beacon.Core no longer references Microsoft.EntityFrameworkCore.SqlServer or EFCore.BulkExtensions. A host on SQL Server already references Moberg.Beacon.Core.SqlServer. Add your own reference only if your own code used those packages through Beacon. The same goes for AWSSDK.BedrockRuntime and the ONNX packages, which Moberg.Beacon.AI no longer brings.
  • Code that calls Beacon’s internals:
    • SqlGateRequest takes a RowSecurity argument, which you get from IRowSecurityResolver.
    • The LlmProviderFactory(accessor, logger) constructor is obsolete. Resolve the provider registrations from DI.
    • Constants.Migration.MaxFailedRowsBeforeStop is gone.
    • IMigrationBulkContextProvider, MigrationBulkContextFactory and DynamicDbContext are removed with EFCore.BulkExtensions, and SqlServerDataTransferManager and ObjectDataReader<T> with the old SQL Server bulk path. Migration writes use the connectors’ IMigrationBulkWriter.
    • BedrockProvider moved to the Beacon.AI.Bedrock assembly and IHostEndpointDispatchMiddleware to Beacon.MCP. Both keep their namespaces: recompile.
    • SqlClientTransportInspector has an abstract Read(string) method, so a class of your own that derives from it must implement it.
    • DatabaseAuthorizationProvider’s constructor takes IBeaconRequestPrincipal, and its (IUserManagementService) overload is gone.
    • AuthenticationPathPrefixes.Matches(PathString) is gone: use BeaconRequestPaths.Matches.
AreaIn 5.0
MySQL migration runsAll-or-nothing on InnoDB. A run no longer ends as a partial success. A Truncate-Load that fails after the clear leaves the table empty. Upsert matches on any unique key and no longer needs the CREATE TEMPORARY TABLES privilege.
AI providerChoosing a provider that needs an API key requires typing a key. Saving or testing a provider that is not installed is refused while AI is on.
Admin standingA user who loses the Admin role loses Admin routes on their next request, not at the next session revalidation.
PostgreSQL and SQL Server migration runsAll-or-nothing: the writes and the key lookup share the run’s transaction, with no row-by-row fallback. An Upsert into a table without a primary key fails. An unqualified SQL Server table means dbo.
SQL Server migration InsertKeeps source identity values and NULLs (writing identity values needs ALTER on the table) and checks CHECK and foreign-key constraints: a violating row fails the run.
XLSX attachmentsDates keep a date format per cell, DateOnly and DateTimeOffset are written as dates, and a report with no rows is an empty workbook instead of an error.

These changes are on main and ship in the next release after 5.0.0.

OpenTelemetry: the MCP SDK’s spans need AddBeaconMcpSdkTracing()

Section titled “OpenTelemetry: the MCP SDK’s spans need AddBeaconMcpSdkTracing()”

AddBeaconInstrumentation() on the tracer provider no longer subscribes to the MCP SDK’s own tools/call spans (source Experimental.ModelContextProtocol); it adds only Beacon’s "Beacon" source. A host that upgrades loses those spans until it also calls AddBeaconMcpSdkTracing() on the same builder, which adds the source and clears the status descriptions the SDK writes (they can carry an error result’s content). Do not add Experimental.ModelContextProtocol yourself: that bypasses the scrubber. The meter extension is unchanged.

OpenAI and Azure OpenAI now go over plain HTTP, as Claude already did, so Moberg.Beacon.AI no longer brings the OpenAI, Azure.AI.OpenAI or Azure.Core packages. Requests keep the SDKs’ wire format and retry policy (up to three retries on 408, 429 and 5xx, honouring Retry-After up to 60 seconds). Redirects are not followed, so an API key never travels to a redirect target. Failures name the HTTP status and the provider’s error code, never the response body, and the request queue does not resend a call that failed with a status, since the provider already retried it.

This also fixes Azure OpenAI, which failed on every call in 4.x and 5.0.0 with a MissingMethodException.

When you upgrade:

  • Packages no longer transitive. If your own code used OpenAI, Azure.AI.OpenAI or Azure.Core through Beacon, reference them yourself.
  • Code that builds providers itself: OpenAiProvider and AzureOpenAiProvider keep their constructors but are obsolete. Providers are built through the ILlmProviderRegistrations that AddBeaconAI() installs, and a host’s own registration for a built-in provider now wins whatever the call order.

A step parameter’s placeholder must be {identifier}: letters, digits and underscore, starting with a letter or underscore (so {1} is refused, although the editor’s auto-detect accepts digits). An empty placeholder is refused, it must be distinct within the step, and a multi-step save now also requires every placeholder to occur in its step’s SQL. Beacon checks it when a query is created or edited, when an AI actor proposes a change and when a query is newly exposed as an MCP tool. Stored queries keep running as they are, and restoring an old version keeps its placeholders as stored, also on an exposed tool. An already-exposed tool keeps running and can be edited or disabled; only newly exposing it needs the shape. The next edit of a stored query asks for a valid placeholder.

When you upgrade:

  • Hosts or scripts that create queries through the API: send {name} placeholders. A bare word, a quoted or spaced placeholder is refused with the parameter’s name.

Stronger password hashes, checked user names

Section titled “Stronger password hashes, checked user names”

Internal users’ passwords are hashed at 600,000 PBKDF2 iterations, and the count is stored in the hash. Existing hashes keep working and are upgraded at each user’s next successful password sign-in (Password hashes). Creating a user, and first-run setup, now trim the user name and e-mail address and refuse a user name outside 3 to 100 characters or with control characters, an e-mail address that is not one plain address, and (setup) a password confirmation that differs. Editing a user keeps a value sent back exactly as stored, and trims and checks only the fields that change.

When you upgrade:

  • Rollback: an earlier version cannot read an upgraded hash. After a rollback, reset the passwords of internal users who signed in on this version.
  • Scripts that create users through the API: send a user name of at least 3 characters and a plain e-mail address (or none).

Security headers and an origin check on Beacon’s routes

Section titled “Security headers and an origin check on Beacon’s routes”

Beacon’s responses now carry X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy: strict-origin and a Permissions-Policy, and responses under /beacon a Content-Security-Policy that allows scripts, styles and workers from Beacon’s own origin only and no framing. A new origin check looks at state-changing, realtime-hub and anonymous Beacon requests and, in this release, only logs the ones from a foreign origin. UseBeaconAuthentication() adds both; your own routes are untouched. See Security headers and the origin check.

When you upgrade:

  • Hosts that frame Beacon or load it into another page’s content: frame-ancestors 'none' and X-Frame-Options: DENY now refuse it. Send your own policy on those responses, or set Beacon:Security:Csp to ReportOnly.
  • Behind a proxy: set Beacon:PublicBaseUrl (or Beacon:Security:AllowedOrigins), watch the log for the origin check’s warnings, then set Beacon:Security:OriginGuard to Enforce. A later release is planned to make Enforce the default.
  • Optional: call app.UseBeaconSecurityHeaders() before UseStaticFiles() so the UI’s files carry the headers too.

A new data retention job can keep run history and logs bounded (see Data retention): with Beacon:Retention:Days set it deletes rows of subscription runs, notifications, anomaly baselines and events, AI actor runs and plans, migration runs, MCP eval runs and the execution log older than the window (90 days recommended; the MCP audit’s own window, 365 days recommended). Beacon:Retention:ContentDays (30 recommended for regulated deployments) clears the SQL text, result snapshots and model output of older rows, and Beacon:Retention:Locked refuses any window that keeps data forever. Every window defaults to keep forever. The content lock (RetainQueryContent off globally, Beacon:Mcp:ForceNoContentRetention) now also covers those stores outside MCP.

When you upgrade:

  • Nothing is deleted on upgrade. Retention is off until you set a window; while none is set the host logs one Information at startup (RetentionUnconfigured). Set the recommended windows to start purging.
  • Schedule the job. Hosts with their own scheduler call IDataRetentionService.RunAsync daily; the sample host runs it as the Warp job data-retention.
  • Content lock wider. With RetainQueryContent off globally or ForceNoContentRetention on, subscription result snapshots, the SQL text of runs and of the execution log, AI actor output and migration statements are no longer stored, and result snapshots stored before are cleared by the retention job.

Every declared foreign key becomes a relationship

Section titled “Every declared foreign key becomes a relationship”

A column that takes part in more than one foreign key (for example tenant_id, referencing tenants on its own and x together with x_id) now yields a relationship for each of them, so the composite key keeps both of its column pairs. Before, Beacon kept one foreign key per column, and which one depended on the order the catalog returned. This applies to SQL Server, Azure Synapse, PostgreSQL and MySQL sources. The column’s own foreign-key fields keep naming one target: the foreign key whose constraint name sorts first.

When you upgrade:

  • Data sources with overlapping foreign keys: more foreign-key relationships appear after the next metadata refresh. Nothing changes until a source is refreshed, and no relationship is removed.

The full list of changes is on the 5.0.0.0 release.