Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1835be5
[MCP] Prototype AL Query Server (Preview) feature in MCP configuratio…
onbuyuka May 6, 2026
36d32d0
Merge remote-tracking branch 'origin/main' into private/onbuyuka/6310…
onbuyuka May 11, 2026
c9cbb4a
[MCP] Add mock facade APIs for AL Query Server (Preview)
onbuyuka May 11, 2026
b8e124e
[MCP] Drop AL Query Server design/mock markdown docs
onbuyuka May 12, 2026
59fe84d
Merge remote-tracking branch 'origin/main' into private/onbuyuka/6310…
onbuyuka May 28, 2026
3fe6525
[MCP] Iterate AL Query Tools UX (rename, Dynamic Tool Mode back as fe…
onbuyuka May 29, 2026
be58cfb
[MCP] Add API Tools as a Server Feature; rename sub-part to "Availabl…
onbuyuka May 29, 2026
70a4a06
[MCP] Refactor Server Features to interface-based handlers
onbuyuka May 29, 2026
fb83ac6
[MCP] Iterate Server Features interface: rename, impl delegation, sys…
onbuyuka May 29, 2026
d856bb2
[MCP] Add Server Features tests; fix stale card TestPage assertions
onbuyuka May 29, 2026
bf28b17
Merge branch 'main' of https://github.com/microsoft/BCApps into priva…
onbuyuka Jun 1, 2026
d71487c
[MCP] Mock-persist feature activation; enforce Dynamic Tool Mode gate
onbuyuka Jun 1, 2026
217071e
[MCP] Mock-comment cleanup + shorten Read-Only Objects caption
onbuyuka Jun 1, 2026
49b399f
[MCP] Renumber MCP Feature Activation table 8360 -> 8356
onbuyuka Jun 1, 2026
31cd774
[MCP] Config UX feedback + quality-pass cleanup
onbuyuka Jun 2, 2026
be70b85
[MCP] Pre-stage productionized code (commented) against BC-Platform #…
onbuyuka Jun 3, 2026
2f3e8b8
[MCP] Productionize Server Features against BC-Platform #44811
onbuyuka Jun 4, 2026
6bbb977
Merge branch 'main' of https://github.com/microsoft/BCApps into priva…
onbuyuka Jun 4, 2026
c660951
[MCP] Address PR review: telemetry dimensions, API Tools disable casc…
onbuyuka Jun 4, 2026
77eff29
[MCP] Obsolete per-type lookup pages instead of deleting them (AS0029)
onbuyuka Jun 4, 2026
5c3e69f
Merge branch 'main' of https://github.com/microsoft/BCApps into priva…
onbuyuka Jun 4, 2026
b9eb271
Apply suggestion from @onbuyuka
onbuyuka Jun 7, 2026
ef20fde
Merge branch 'main' into private/onbuyuka/631012-al-query-mcp-config-ui
onbuyuka Jul 6, 2026
70be5a9
Address PR review: add missing tooltips, align API terminology, fix a…
onbuyuka Jul 6, 2026
dd77184
Delete docs/features/al-query-mcp-config-ui/design.md
onbuyuka Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ permissionset 8350 "MCP - Objects"
table "MCP Configuration Tool" = X,
table "MCP Config Warning" = X,
table "MCP Entra Application" = X,
table "MCP System Tool" = X;
table "MCP System Tool" = X,
table "MCP Server Feature" = X,
table "MCP API Object Buffer" = X;
}
6 changes: 5 additions & 1 deletion src/System Application/App/MCP/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,15 @@
"idRanges": [
{
"from": 8350,
"to": 8360
"to": 8362
},
{
"from": 8365,
"to": 8370
},
{
"from": 8376,
Comment thread
onbuyuka marked this conversation as resolved.
"to": 8380
}
],
"target": "OnPrem",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------

namespace System.MCP;

codeunit 8369 "MCP API Tools Feature" implements "MCP Server Features"
{
Access = Internal;

procedure SetActive(ConfigId: Guid; Active: Boolean)
var
MCPConfigImplementation: Codeunit "MCP Config Implementation";
begin
MCPConfigImplementation.EnableAPITools(ConfigId, Active);
end;

procedure IsActive(ConfigId: Guid): Boolean
var
MCPConfigImplementation: Codeunit "MCP Config Implementation";
begin
exit(MCPConfigImplementation.IsAPIToolsEnabled(ConfigId));
end;

procedure HasSettings(): Boolean
begin
exit(false);
end;

procedure OpenSettings(ConfigId: Guid)
begin
// No configurable settings.
end;

procedure Description(): Text[500]
begin
exit(DescriptionLbl);
end;

procedure LoadSystemTools(var MCPSystemTool: Record "MCP System Tool")
begin
// API Tools exposes no system tools.
end;

procedure TryGetParentFeature(var ParentFeature: Enum "MCP Server Feature"): Boolean
begin
exit(false);
end;

var
DescriptionLbl: Label 'Exposes the API Tools list on this configuration so the admin can curate which API pages and queries the MCP client can reach. Dynamic Tool Mode requires this feature to be enabled.';
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ codeunit 8350 "MCP Config"
MCPConfigImplementation.EnableDiscoverReadOnlyObjects(ConfigId, Enable);
end;

/// <summary>
/// Enables or disables the Data Query Tools feature for the specified configuration.
/// </summary>
/// <param name="ConfigId">The SystemId (GUID) of the configuration.</param>
/// <param name="Enable">True to enable, false to disable.</param>
procedure EnableDataQueryTools(ConfigId: Guid; Enable: Boolean)
begin
MCPConfigImplementation.EnableDataQueryTools(ConfigId, Enable);
end;

/// <summary>
/// Enables or disables the API Tools feature for the specified configuration.
/// </summary>
/// <param name="ConfigId">The SystemId (GUID) of the configuration.</param>
/// <param name="Enable">True to enable, false to disable.</param>
procedure EnableAPITools(ConfigId: Guid; Enable: Boolean)
begin
MCPConfigImplementation.EnableAPITools(ConfigId, Enable);
end;

/// <summary>
/// Finds warnings for the specified MCP configuration, such as missing objects or missing parent objects.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ codeunit 8351 "MCP Config Implementation"
DesignatedDefaultCannotBeDeactivatedErr: Label 'The designated default configuration cannot be deactivated. Clear the default designation first.';
ConfigurationMustBeActiveErr: Label 'Only active configurations can be set as the default.';
DynamicToolModeRequiredErr: Label 'Dynamic tool mode needs to be enabled to discover read-only objects.';
APIToolsRequiredForDynamicErr: Label 'API Tools must be enabled before Dynamic Tool Mode can be enabled.';
VersionNotValidErr: Label 'The API version is not valid for the selected tool.';
MCPConfigurationCreatedLbl: Label 'MCP Configuration created', Locked = true;
MCPConfigurationModifiedLbl: Label 'MCP Configuration modified', Locked = true;
Expand Down Expand Up @@ -224,6 +225,9 @@ codeunit 8351 "MCP Config Implementation"
if not Enable and IsDefaultConfiguration(MCPConfiguration) then
Error(DynamicToolModeCannotBeDisabledErr);

if Enable and not IsAPIToolsEnabled(ConfigId) then
Comment thread
onbuyuka marked this conversation as resolved.
Error(APIToolsRequiredForDynamicErr);

MCPConfiguration.EnableDynamicToolMode := Enable;
if not Enable then
MCPConfiguration.DiscoverReadOnlyObjects := false;
Expand Down Expand Up @@ -252,6 +256,56 @@ codeunit 8351 "MCP Config Implementation"
LogConfigurationModified(MCPConfiguration, xMCPConfiguration);
end;

internal procedure EnableAPITools(ConfigId: Guid; Enable: Boolean)
Comment thread
onbuyuka marked this conversation as resolved.
Comment thread
onbuyuka marked this conversation as resolved.
var
MCPConfiguration: Record "MCP Configuration";
xMCPConfiguration: Record "MCP Configuration";
begin
if not MCPConfiguration.GetBySystemId(ConfigId) then
Error(ConfigurationNotFoundErr);
Comment thread
onbuyuka marked this conversation as resolved.
xMCPConfiguration := MCPConfiguration;
MCPConfiguration.EnableApiTools := Enable;
if not Enable then begin
// Dynamic Tool Mode requires API Tools, so disabling API Tools cascades it off
// (mirroring how disabling Dynamic Tool Mode clears Discover Read-Only Objects).
MCPConfiguration.EnableDynamicToolMode := false;
MCPConfiguration.DiscoverReadOnlyObjects := false;
end;
MCPConfiguration.Modify();
LogConfigurationModified(MCPConfiguration, xMCPConfiguration);
end;

internal procedure EnableDataQueryTools(ConfigId: Guid; Enable: Boolean)
var
MCPConfiguration: Record "MCP Configuration";
xMCPConfiguration: Record "MCP Configuration";
begin
if not MCPConfiguration.GetBySystemId(ConfigId) then
Error(ConfigurationNotFoundErr);
xMCPConfiguration := MCPConfiguration;
MCPConfiguration.EnableAlQueryTools := Enable;
MCPConfiguration.Modify();
LogConfigurationModified(MCPConfiguration, xMCPConfiguration);
end;

internal procedure IsAPIToolsEnabled(ConfigId: Guid): Boolean
Comment thread
onbuyuka marked this conversation as resolved.
Comment thread
onbuyuka marked this conversation as resolved.
var
MCPConfiguration: Record "MCP Configuration";
begin
if not MCPConfiguration.GetBySystemId(ConfigId) then
Comment thread
onbuyuka marked this conversation as resolved.
MCPConfiguration.Init(); // not persisted yet (new config): reflect the table default (InitValue)
exit(MCPConfiguration.EnableApiTools);
end;

internal procedure IsDataQueryToolsEnabled(ConfigId: Guid): Boolean
var
MCPConfiguration: Record "MCP Configuration";
begin
if not MCPConfiguration.GetBySystemId(ConfigId) then
MCPConfiguration.Init(); // not persisted yet (new config): reflect the table default (InitValue)
exit(MCPConfiguration.EnableAlQueryTools);
end;

Comment thread
onbuyuka marked this conversation as resolved.
local procedure CheckAllowCreateUpdateDeleteTools(ConfigId: Guid)
var
MCPConfiguration: Record "MCP Configuration";
Expand Down Expand Up @@ -568,38 +622,65 @@ codeunit 8351 "MCP Config Implementation"
MCPConfigurationTool.Modify();
end;

internal procedure LookupAPIPageTools(var PageMetadata: Record "Page Metadata"): Boolean
internal procedure LookupAPIObjects(var SelectedObjects: Record "MCP API Object Buffer"): Boolean
Comment thread
onbuyuka marked this conversation as resolved.
var
MCPAPIConfigToolLookup: Page "MCP API Config Tool Lookup";
TempMCPAPIObjectBuffer: Record "MCP API Object Buffer";
MCPAPIObjectLookup: Page "MCP API Object Lookup";
begin
PageMetadata.SetRange(PageType, PageMetadata.PageType::API);
PageMetadata.SetFilter("AL Namespace", '<>%1', 'Microsoft.API.V1');
PageMetadata.SetFilter(APIVersion, '<>%1', 'beta');
PopulateAPIObjects(TempMCPAPIObjectBuffer);
if TempMCPAPIObjectBuffer.IsEmpty() then
exit(false);

MCPAPIConfigToolLookup.LookupMode := true;
MCPAPIConfigToolLookup.SetTableView(PageMetadata);
if MCPAPIConfigToolLookup.RunModal() <> Action::LookupOK then
MCPAPIObjectLookup.SetObjects(TempMCPAPIObjectBuffer);
MCPAPIObjectLookup.LookupMode := true;
if MCPAPIObjectLookup.RunModal() <> Action::LookupOK then
exit(false);

MCPAPIConfigToolLookup.SetSelectionFilter(PageMetadata);
exit(true);
MCPAPIObjectLookup.GetSelectedObjects(SelectedObjects);
exit(not SelectedObjects.IsEmpty());
end;

internal procedure LookupAPIQueryTools(var QueryMetadata: Record "Query Metadata"): Boolean
local procedure PopulateAPIObjects(var MCPAPIObjectBuffer: Record "MCP API Object Buffer")
Comment thread
onbuyuka marked this conversation as resolved.
var
MCPQueryConfigToolLookup: Page "MCP Query Config Tool Lookup";
PageMetadata: Record "Page Metadata";
QueryMetadata: Record "Query Metadata";
begin
MCPAPIObjectBuffer.Reset();
MCPAPIObjectBuffer.DeleteAll();

// API pages
PageMetadata.SetRange(PageType, PageMetadata.PageType::API);
Comment thread
onbuyuka marked this conversation as resolved.
PageMetadata.SetFilter("AL Namespace", '<>%1', 'Microsoft.API.V1');
PageMetadata.SetFilter(APIVersion, '<>%1', 'beta');
if PageMetadata.FindSet() then
repeat
MCPAPIObjectBuffer.Init();
MCPAPIObjectBuffer."Object Type" := MCPAPIObjectBuffer."Object Type"::Page;
MCPAPIObjectBuffer."Object ID" := PageMetadata.ID;
MCPAPIObjectBuffer.Name := CopyStr(PageMetadata.Name, 1, MaxStrLen(MCPAPIObjectBuffer.Name));
MCPAPIObjectBuffer."Entity Name" := CopyStr(PageMetadata.EntityName, 1, MaxStrLen(MCPAPIObjectBuffer."Entity Name"));
MCPAPIObjectBuffer."API Publisher" := CopyStr(PageMetadata.APIPublisher, 1, MaxStrLen(MCPAPIObjectBuffer."API Publisher"));
MCPAPIObjectBuffer."API Group" := CopyStr(PageMetadata.APIGroup, 1, MaxStrLen(MCPAPIObjectBuffer."API Group"));
MCPAPIObjectBuffer."API Version" := CopyStr(PageMetadata.APIVersion, 1, MaxStrLen(MCPAPIObjectBuffer."API Version"));
if MCPAPIObjectBuffer.Insert() then;
until PageMetadata.Next() = 0;

// API queries
QueryMetadata.SetFilter(EntityName, '<>%1', '');
QueryMetadata.SetFilter("AL Namespace", '<>%1', 'Microsoft.API.V1');
QueryMetadata.SetFilter(ID, '<>%1&<>%2', 5480, 5481); // Exclude beta customer and vendor queries from Base Application, as they are already part of API v2.0

MCPQueryConfigToolLookup.LookupMode := true;
MCPQueryConfigToolLookup.SetTableView(QueryMetadata);
if MCPQueryConfigToolLookup.RunModal() <> Action::LookupOK then
exit(false);

MCPQueryConfigToolLookup.SetSelectionFilter(QueryMetadata);
exit(true);
if QueryMetadata.FindSet() then
repeat
MCPAPIObjectBuffer.Init();
MCPAPIObjectBuffer."Object Type" := MCPAPIObjectBuffer."Object Type"::Query;
MCPAPIObjectBuffer."Object ID" := QueryMetadata.ID;
MCPAPIObjectBuffer.Name := CopyStr(QueryMetadata.Name, 1, MaxStrLen(MCPAPIObjectBuffer.Name));
MCPAPIObjectBuffer."Entity Name" := CopyStr(QueryMetadata.EntityName, 1, MaxStrLen(MCPAPIObjectBuffer."Entity Name"));
MCPAPIObjectBuffer."API Publisher" := CopyStr(QueryMetadata.APIPublisher, 1, MaxStrLen(MCPAPIObjectBuffer."API Publisher"));
MCPAPIObjectBuffer."API Group" := CopyStr(QueryMetadata.APIGroup, 1, MaxStrLen(MCPAPIObjectBuffer."API Group"));
MCPAPIObjectBuffer."API Version" := CopyStr(QueryMetadata.APIVersion, 1, MaxStrLen(MCPAPIObjectBuffer."API Version"));
if MCPAPIObjectBuffer.Insert() then;
until QueryMetadata.Next() = 0;
end;

internal procedure GetAPIPublishers(var MCPAPIPublisherGroup: Record "MCP API Publisher Group")
Expand Down Expand Up @@ -850,27 +931,6 @@ codeunit 8351 "MCP Config Implementation"
exit('');
end;

internal procedure LoadSystemTools(var MCPSystemTool: Record "MCP System Tool")
var
MCPUtilities: Codeunit "MCP Utilities";
SystemTools: Dictionary of [Text, Text];
ToolName: Text;
begin
MCPSystemTool.Reset();
MCPSystemTool.DeleteAll();

SystemTools := MCPUtilities.GetSystemToolsInDynamicMode();
foreach ToolName in SystemTools.Keys() do
InsertSystemTool(MCPSystemTool, CopyStr(ToolName, 1, MaxStrLen(MCPSystemTool."Tool Name")), CopyStr(SystemTools.Get(ToolName), 1, MaxStrLen(MCPSystemTool."Tool Description")));
end;

local procedure InsertSystemTool(var MCPSystemTool: Record "MCP System Tool"; ToolName: Text[100]; ToolDescription: Text[250])
begin
MCPSystemTool."Tool Name" := ToolName;
MCPSystemTool."Tool Description" := ToolDescription;
MCPSystemTool.Insert();
end;

internal procedure ValidateAPIPageVersion(ObjectId: Integer; APIVersion: Text)
var
PageMetadata: Record "Page Metadata";
Expand Down Expand Up @@ -1213,6 +1273,8 @@ codeunit 8351 "MCP Config Implementation"
ConfigJson.Add('enableDynamicToolMode', MCPConfiguration.EnableDynamicToolMode);
ConfigJson.Add('discoverReadOnlyObjects', MCPConfiguration.DiscoverReadOnlyObjects);
ConfigJson.Add('allowProdChanges', MCPConfiguration.AllowProdChanges);
ConfigJson.Add('enableApiTools', MCPConfiguration.EnableApiTools);
ConfigJson.Add('enableAlQueryTools', MCPConfiguration.EnableAlQueryTools);

MCPConfigurationTool.SetRange(ID, ConfigId);
if MCPConfigurationTool.FindSet() then
Expand Down Expand Up @@ -1280,6 +1342,12 @@ codeunit 8351 "MCP Config Implementation"
if ConfigJson.Contains('allowProdChanges') then
MCPConfiguration.AllowProdChanges := ConfigJson.GetBoolean('allowProdChanges');

if ConfigJson.Contains('enableApiTools') then
MCPConfiguration.EnableApiTools := ConfigJson.GetBoolean('enableApiTools');

if ConfigJson.Contains('enableAlQueryTools') then
MCPConfiguration.EnableAlQueryTools := ConfigJson.GetBoolean('enableAlQueryTools');

MCPConfiguration.Insert();
LogConfigurationCreated(MCPConfiguration);

Expand Down Expand Up @@ -1411,6 +1479,14 @@ codeunit 8351 "MCP Config Implementation"
Dimensions.Add('OldDiscoverReadOnlyObjects', Format(xMCPConfiguration.DiscoverReadOnlyObjects));
Dimensions.Add('NewDiscoverReadOnlyObjects', Format(MCPConfiguration.DiscoverReadOnlyObjects));
end;
if MCPConfiguration.EnableApiTools <> xMCPConfiguration.EnableApiTools then begin
Dimensions.Add('OldApiTools', Format(xMCPConfiguration.EnableApiTools));
Dimensions.Add('NewApiTools', Format(MCPConfiguration.EnableApiTools));
end;
if MCPConfiguration.EnableAlQueryTools <> xMCPConfiguration.EnableAlQueryTools then begin
Dimensions.Add('OldDataQueryTools', Format(xMCPConfiguration.EnableAlQueryTools));
Dimensions.Add('NewDataQueryTools', Format(MCPConfiguration.EnableAlQueryTools));
end;
Session.LogMessage('0000QE9', MCPConfigurationModifiedLbl, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, Dimensions);
Session.LogAuditMessage(StrSubstNo(MCPConfigurationAuditModifiedLbl, MCPConfiguration.Name, UserSecurityId(), CompanyName()), SecurityOperationResult::Success, AuditCategory::ApplicationManagement, 3, 0);
end;
Expand Down
Loading
Loading