Kandra.Mcp
Kandra.Mcp.Auth.ApiKeyOnlyAuthorizationHandler
Succeeds only when the authenticated principal carries the token_kind=apikey claim ApiKeyAuthenticator stamps on every API-key-derived principal. Checking the claim (rather than restricting the endpoint's authentication scheme directly to ApiKeyConstants.Scheme) keeps KandraAuthenticationSchemes.Default's JWT-or-API-key forwarding intact, so a JWT caller still gets authenticated (then cleanly 403s here) instead of the policy scheme silently never trying JWT at all.
Kandra.Mcp.Auth.ApiKeyOnlyRequirement
Requires the caller to have authenticated via an API key (Kandra.Constants.ApiKeyConstants), rejecting a JWT-authenticated (human) caller - the MCP endpoint intentionally never accepts JWT, per docs/kandra-architecture.md §8.1.
Kandra.Mcp.KandraMcpEndpointRouteBuilderExtensions
Methods
| Method | Description |
|---|---|
MapKandraMcp(IEndpointRouteBuilder, String) | Maps the MCP HTTP endpoint at pattern, gated by the API-key-only authorization policy (ApiKeyOnlyRequirement) - a JWT-authenticated (human) caller gets 403, an unauthenticated caller gets 401, per docs/kandra-architecture.md §8.1. |
Kandra.Mcp.KandraMcpServiceCollectionExtensions
Methods
| Method | Description |
|---|---|
AddKandraMcp<T0>(IServiceCollection, String) | Wires the engine-generic half of the MCP server: the entity catalog (composed from the per-configuration generated TCatalog plus the already-registered IConstantRegistry), the dynamic meta-tools, the blob tools, and the API-key-only authorization policy. Deliberately separate from Kandra.Api.Lib's AddKandra/ UseKandra - MCP exposure is a per-deployment opt-in, not something every configuration gets unconditionally (see this project's own design notes). A configuration also needs to call its own generated AddGenerated{Config}EntityAdapters() (emitted by Kandra.Generators.EntityOperations into its Application project) and, after building the host, MapKandraMcp. |
Kandra.Mcp.RateLimiting.INotificationRateLimiter
Per-API-key throttle for the send_notification MCP tool - a misbehaving or overly chatty agent shouldn't be able to flood every user's inbox. Fixed-hourly-window counter, sufficient for this project's single-server target (no distributed limiter needed - see the notifications design doc §4.2).
Methods
| Method | Description |
|---|---|
TryAcquire(Guid) | Returns false (and reserves nothing) if apiKeyId has already reached its hourly cap; otherwise counts this call against the window and returns true. |
Kandra.Mcp.RateLimiting.InMemoryNotificationRateLimiter
Registered singleton - the fixed window has to live longer than any one scoped MCP request. One WindowState per API key id, lock-protected since two calls from the same key can race concurrently; other keys never contend with each other.
Constructors
| Constructor | Description |
|---|---|
InMemoryNotificationRateLimiter(TimeProvider) | Registered singleton - the fixed window has to live longer than any one scoped MCP request. One WindowState per API key id, lock-protected since two calls from the same key can race concurrently; other keys never contend with each other. |
Kandra.Mcp.Tools.AccountingTools
Tool 9 (get_chart_of_accounts) - lets an AI agent understand the chart-of-accounts structure before constructing an AccountTransaction (a manual debit/credit posting, kind=Document). Wraps the already engine-registered, configuration-agnostic IChartOfAccountsTreeService (the same service the Blazor client's account/subconto picker UI reads), and cross-references each subconto slot's declared type against the MCP entity catalog by its [TypeId] - the same Guid identity both the chart and every Dictionary/Document/Enum Dto already carry - so a slot's requirement is reported as "Kind:TypeName", directly usable as execute_entity_operation's own kind/typeName arguments to look up a valid value id.
Constructors
| Constructor | Description |
|---|---|
AccountingTools(IChartOfAccountsTreeService, IEntityCatalog, IStringLocalizer) | Tool 9 (get_chart_of_accounts) - lets an AI agent understand the chart-of-accounts structure before constructing an AccountTransaction (a manual debit/credit posting, kind=Document). Wraps the already engine-registered, configuration-agnostic IChartOfAccountsTreeService (the same service the Blazor client's account/subconto picker UI reads), and cross-references each subconto slot's declared type against the MCP entity catalog by its [TypeId] - the same Guid identity both the chart and every Dictionary/Document/Enum Dto already carry - so a slot's requirement is reported as "Kind:TypeName", directly usable as execute_entity_operation's own kind/typeName arguments to look up a valid value id. |
Methods
| Method | Description |
|---|---|
BuildTypeIdIndex | One catalog entry per subconto-referenceable [TypeId] - a hierarchical entry is keyed by its root Dto's [TypeId], the dictionary's one TypeId shared by folder and leaf, which is what a subconto slot's TypeId matches (subconto only ever holds a leaf value, e.g. a Counterparty, but the leaf inherits the root's TypeId rather than carrying its own). |
Kandra.Mcp.Tools.BlobTools
Tools 7 (upload_blob) and 8 (download_blob) - thin wrappers over IBlobService, entirely independent of the entity catalog/adapters. Base64-inline content, not an MCP resource reference - unnecessary extra machinery for what are typically small attachments. A blob uploaded here is attached to an entity by putting its returned id into the target Dto's [FilePicker] property on a subsequent Create/Update call via execute_entity_operation.
Constructors
| Constructor | Description |
|---|---|
BlobTools(IBlobService) | Tools 7 (upload_blob) and 8 (download_blob) - thin wrappers over IBlobService, entirely independent of the entity catalog/adapters. Base64-inline content, not an MCP resource reference - unnecessary extra machinery for what are typically small attachments. A blob uploaded here is attached to an entity by putting its returned id into the target Dto's [FilePicker] property on a subsequent Create/Update call via execute_entity_operation. |
Kandra.Mcp.Tools.EntityKindCatalog
Fixed, hand-written per-kind explanation/operation vocabulary for tools 2 (list_entity_kinds) and 3 (describe_entity_kind) - structural facts about the entity-kind system (docs/kandra-architecture.md §2.1's capability matrix), not derived from any per-configuration catalog.
Kandra.Mcp.Tools.EntityMetaTools
Tools 3 (describe_entity_kind), 4 (list_entity_types), 5 (describe_entity_type).
Constructors
| Constructor | Description |
|---|---|
EntityMetaTools(IEntityCatalog, EntityFieldMetadataBuilder, IConstantsService) | Tools 3 (describe_entity_kind), 4 (list_entity_types), 5 (describe_entity_type). |
Kandra.Mcp.Tools.EntityOperationTools
Tool 6 (execute_entity_operation) - the single generic dispatch point onto every entity's CRUD/report/processor/constant service.
Constructors
| Constructor | Description |
|---|---|
EntityOperationTools(IEntityOperationExecutor) | Tool 6 (execute_entity_operation) - the single generic dispatch point onto every entity's CRUD/report/processor/constant service. |
Kandra.Mcp.Tools.NotificationMcpTools
Tools 10 (send_notification) and 11 (find_users) - lets an AI agent notify a human, e.g. after finishing a long-running task on their behalf (design: docs/kandra-notifications-and-messaging-design.md §4). Gated by NotificationsAuthorizationPoint - a permission an admin grants to a role/API-key owner separately from the broader "can call MCP at all" check (ApiKeyOnlyAuthorizationHandler), and independent of INotificationService itself, which stays ungated for trusted in-process producers. send_notification only ever calls NotifyUsersAsync - never NotifyRoleAsync/NotifyWithPermissionAsync - so an agent can reach a caller-chosen list of users but never a broadcast audience.
Constructors
| Constructor | Description |
|---|---|
NotificationMcpTools(INotificationService, ILookupService, IUnitOfWork, ICallerContext, IAuthorizationChecker, INotificationRateLimiter, IEntityCatalog) | Tools 10 (send_notification) and 11 (find_users) - lets an AI agent notify a human, e.g. after finishing a long-running task on their behalf (design: docs/kandra-notifications-and-messaging-design.md §4). Gated by NotificationsAuthorizationPoint - a permission an admin grants to a role/API-key owner separately from the broader "can call MCP at all" check (ApiKeyOnlyAuthorizationHandler), and independent of INotificationService itself, which stays ungated for trusted in-process producers. send_notification only ever calls NotifyUsersAsync - never NotifyRoleAsync/NotifyWithPermissionAsync - so an agent can reach a caller-chosen list of users but never a broadcast audience. |
Kandra.Mcp.Tools.SystemTools
Tools 1 (describe_system) and 2 (list_entity_kinds) - static, no per-caller state.