diff --git a/src/System Application/App/MCP/Permissions/MCPObjects.PermissionSet.al b/src/System Application/App/MCP/Permissions/MCPObjects.PermissionSet.al
index fbbf1b728c..a2f6d58496 100644
--- a/src/System Application/App/MCP/Permissions/MCPObjects.PermissionSet.al
+++ b/src/System Application/App/MCP/Permissions/MCPObjects.PermissionSet.al
@@ -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;
}
diff --git a/src/System Application/App/MCP/app.json b/src/System Application/App/MCP/app.json
index e6449e6242..f9b18ba691 100644
--- a/src/System Application/App/MCP/app.json
+++ b/src/System Application/App/MCP/app.json
@@ -72,11 +72,15 @@
"idRanges": [
{
"from": 8350,
- "to": 8360
+ "to": 8362
},
{
"from": 8365,
"to": 8370
+ },
+ {
+ "from": 8376,
+ "to": 8380
}
],
"target": "OnPrem",
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPApiToolsFeature.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPApiToolsFeature.Codeunit.al
new file mode 100644
index 0000000000..d3d3a32e11
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPApiToolsFeature.Codeunit.al
@@ -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.';
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfig.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfig.Codeunit.al
index 2fe1e03ac9..457a5fc34d 100644
--- a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfig.Codeunit.al
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfig.Codeunit.al
@@ -117,6 +117,26 @@ codeunit 8350 "MCP Config"
MCPConfigImplementation.EnableDiscoverReadOnlyObjects(ConfigId, Enable);
end;
+ ///
+ /// Enables or disables the Data Query Tools feature for the specified configuration.
+ ///
+ /// The SystemId (GUID) of the configuration.
+ /// True to enable, false to disable.
+ procedure EnableDataQueryTools(ConfigId: Guid; Enable: Boolean)
+ begin
+ MCPConfigImplementation.EnableDataQueryTools(ConfigId, Enable);
+ end;
+
+ ///
+ /// Enables or disables the API Tools feature for the specified configuration.
+ ///
+ /// The SystemId (GUID) of the configuration.
+ /// True to enable, false to disable.
+ procedure EnableAPITools(ConfigId: Guid; Enable: Boolean)
+ begin
+ MCPConfigImplementation.EnableAPITools(ConfigId, Enable);
+ end;
+
///
/// Finds warnings for the specified MCP configuration, such as missing objects or missing parent objects.
///
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfigImplementation.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfigImplementation.Codeunit.al
index 2ab59c376f..6f48880e74 100644
--- a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfigImplementation.Codeunit.al
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPConfigImplementation.Codeunit.al
@@ -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;
@@ -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
+ Error(APIToolsRequiredForDynamicErr);
+
MCPConfiguration.EnableDynamicToolMode := Enable;
if not Enable then
MCPConfiguration.DiscoverReadOnlyObjects := false;
@@ -252,6 +256,56 @@ codeunit 8351 "MCP Config Implementation"
LogConfigurationModified(MCPConfiguration, xMCPConfiguration);
end;
+ internal procedure EnableAPITools(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.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
+ 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.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;
+
local procedure CheckAllowCreateUpdateDeleteTools(ConfigId: Guid)
var
MCPConfiguration: Record "MCP Configuration";
@@ -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
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")
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);
+ 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")
@@ -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";
@@ -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
@@ -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);
@@ -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;
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDataQueryToolsFeature.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDataQueryToolsFeature.Codeunit.al
new file mode 100644
index 0000000000..a757615d5c
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDataQueryToolsFeature.Codeunit.al
@@ -0,0 +1,67 @@
+// ------------------------------------------------------------------------------------------------
+// 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 8368 "MCP Data Query Tools Feature" implements "MCP Server Features"
+{
+ Access = Internal;
+
+ procedure SetActive(ConfigId: Guid; Active: Boolean)
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ MCPConfigImplementation.EnableDataQueryTools(ConfigId, Active);
+ end;
+
+ procedure IsActive(ConfigId: Guid): Boolean
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ exit(MCPConfigImplementation.IsDataQueryToolsEnabled(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")
+ var
+ MCPUtilities: Codeunit "MCP Utilities";
+ SystemTools: Dictionary of [Text, Text];
+ ToolName: Text;
+ begin
+ SystemTools := MCPUtilities.GetSystemToolsInDataQuery();
+ foreach ToolName in SystemTools.Keys() do
+ InsertTool(MCPSystemTool, CopyStr(ToolName, 1, MaxStrLen(MCPSystemTool."Tool Name")), CopyStr(SystemTools.Get(ToolName), 1, MaxStrLen(MCPSystemTool."Tool Description")));
+ end;
+
+ procedure TryGetParentFeature(var ParentFeature: Enum "MCP Server Feature"): Boolean
+ begin
+ exit(false);
+ end;
+
+ local procedure InsertTool(var MCPSystemTool: Record "MCP System Tool"; ToolName: Text[100]; ToolDescription: Text[250])
+ begin
+ MCPSystemTool."Server Feature" := MCPSystemTool."Server Feature"::"Data Query Tools";
+ MCPSystemTool."Tool Name" := ToolName;
+ MCPSystemTool."Tool Description" := ToolDescription;
+ MCPSystemTool.Insert();
+ end;
+
+ var
+ DescriptionLbl: Label 'Adds system tools that compile and run data query code submitted by the client on demand, letting agents author ad-hoc joins and aggregates that no pre-defined API query covers. API queries and API pages added to Available APIs are exposed independently and are not affected by this feature.';
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDynToolModeFeature.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDynToolModeFeature.Codeunit.al
new file mode 100644
index 0000000000..76272fa041
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPDynToolModeFeature.Codeunit.al
@@ -0,0 +1,75 @@
+// ------------------------------------------------------------------------------------------------
+// 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 8370 "MCP Dyn. Tool Mode Feature" implements "MCP Server Features"
+{
+ Access = Internal;
+
+ procedure SetActive(ConfigId: Guid; Active: Boolean)
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ MCPConfigImplementation.EnableDynamicToolMode(ConfigId, Active);
+ end;
+
+ procedure IsActive(ConfigId: Guid): Boolean
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ begin
+ if not MCPConfiguration.GetBySystemId(ConfigId) then
+ exit(false);
+ exit(MCPConfiguration.EnableDynamicToolMode);
+ end;
+
+ procedure HasSettings(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure OpenSettings(ConfigId: Guid)
+ var
+ ServerFeatureSettings: Page "MCP Server Feature Settings";
+ begin
+ ServerFeatureSettings.SetContext(ConfigId, "MCP Server Feature"::"Dynamic Tool Mode");
+ if ServerFeatureSettings.RunModal() = Action::OK then
+ ServerFeatureSettings.SaveChanges();
+ end;
+
+ procedure Description(): Text[500]
+ begin
+ exit(DescriptionLbl);
+ end;
+
+ procedure LoadSystemTools(var MCPSystemTool: Record "MCP System Tool")
+ var
+ MCPUtilities: Codeunit "MCP Utilities";
+ SystemTools: Dictionary of [Text, Text];
+ ToolName: Text;
+ begin
+ SystemTools := MCPUtilities.GetSystemToolsInDynamicMode();
+ foreach ToolName in SystemTools.Keys() do
+ InsertTool(MCPSystemTool, CopyStr(ToolName, 1, MaxStrLen(MCPSystemTool."Tool Name")), CopyStr(SystemTools.Get(ToolName), 1, MaxStrLen(MCPSystemTool."Tool Description")));
+ end;
+
+ procedure TryGetParentFeature(var ParentFeature: Enum "MCP Server Feature"): Boolean
+ begin
+ // Dynamic Tool Mode is a sub-feature of API Tools.
+ ParentFeature := "MCP Server Feature"::"API Tools";
+ exit(true);
+ end;
+
+ local procedure InsertTool(var MCPSystemTool: Record "MCP System Tool"; ToolName: Text[100]; ToolDescription: Text[250])
+ begin
+ MCPSystemTool."Server Feature" := MCPSystemTool."Server Feature"::"Dynamic Tool Mode";
+ MCPSystemTool."Tool Name" := ToolName;
+ MCPSystemTool."Tool Description" := ToolDescription;
+ MCPSystemTool.Insert();
+ end;
+
+ var
+ DescriptionLbl: Label 'Exposes system tools that let clients search, describe, and invoke the API tools added to Available APIs without each tool being surfaced as its own tool. When inactive, every API tool in Available APIs is exposed directly to clients as its own MCP tool (Static Mode).';
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPUpgrade.Codeunit.al b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPUpgrade.Codeunit.al
index 319b84c37b..1112090b20 100644
--- a/src/System Application/App/MCP/src/Configuration/Codeunits/MCPUpgrade.Codeunit.al
+++ b/src/System Application/App/MCP/src/Configuration/Codeunits/MCPUpgrade.Codeunit.al
@@ -18,6 +18,7 @@ codeunit 8356 "MCP Upgrade"
begin
UpgradeMCPAPIToolVersion();
UpgradeMCPSystemDefaultAsDefault();
+ EnableApiToolsOnExistingConfigurations();
end;
internal procedure UpgradeMCPAPIToolVersion()
@@ -62,11 +63,25 @@ codeunit 8356 "MCP Upgrade"
UpgradeTag.SetUpgradeTag(GetMCPSystemDefaultAsDefaultUpgradeTag());
end;
+ internal procedure EnableApiToolsOnExistingConfigurations()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ UpgradeTag: Codeunit "Upgrade Tag";
+ begin
+ if UpgradeTag.HasDatabaseUpgradeTag(GetMCPEnableApiToolsUpgradeTag()) then
+ exit;
+
+ MCPConfiguration.ModifyAll(EnableApiTools, true);
+
+ UpgradeTag.SetUpgradeTag(GetMCPEnableApiToolsUpgradeTag());
+ end;
+
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Upgrade Tag", OnGetPerDatabaseUpgradeTags, '', false, false)]
local procedure RegisterUpgradeTags(var PerDatabaseUpgradeTags: List of [Code[250]])
begin
PerDatabaseUpgradeTags.Add(GetMCPAPIToolVersionUpgradeTag());
PerDatabaseUpgradeTags.Add(GetMCPSystemDefaultAsDefaultUpgradeTag());
+ PerDatabaseUpgradeTags.Add(GetMCPEnableApiToolsUpgradeTag());
end;
local procedure GetMCPAPIToolVersionUpgradeTag(): Text[250]
@@ -78,4 +93,9 @@ codeunit 8356 "MCP Upgrade"
begin
exit('MS-612454-MCPSystemDefaultAsDefault-20260216');
end;
+
+ local procedure GetMCPEnableApiToolsUpgradeTag(): Text[250]
+ begin
+ exit('MS-631012-MCPEnableApiTools-20260603');
+ end;
}
\ No newline at end of file
diff --git a/src/System Application/App/MCP/src/Configuration/Enums/MCPServerFeature.Enum.al b/src/System Application/App/MCP/src/Configuration/Enums/MCPServerFeature.Enum.al
new file mode 100644
index 0000000000..e4f96715b6
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Enums/MCPServerFeature.Enum.al
@@ -0,0 +1,28 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+enum 8351 "MCP Server Feature" implements "MCP Server Features"
+{
+ Access = Internal;
+ Extensible = false;
+
+ value(0; "API Tools")
+ {
+ Caption = 'API Tools';
+ Implementation = "MCP Server Features" = "MCP API Tools Feature";
+ }
+ value(1; "Dynamic Tool Mode")
+ {
+ Caption = 'Dynamic Tool Mode';
+ Implementation = "MCP Server Features" = "MCP Dyn. Tool Mode Feature";
+ }
+ value(2; "Data Query Tools")
+ {
+ Caption = 'Data Query Tools (Preview)';
+ Implementation = "MCP Server Features" = "MCP Data Query Tools Feature";
+ }
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Interfaces/MCPServerFeatures.Interface.al b/src/System Application/App/MCP/src/Configuration/Interfaces/MCPServerFeatures.Interface.al
new file mode 100644
index 0000000000..592f8bb56d
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Interfaces/MCPServerFeatures.Interface.al
@@ -0,0 +1,47 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+interface "MCP Server Features"
+{
+ Access = Internal;
+
+ ///
+ /// Activates or deactivates the feature for the specified MCP configuration.
+ ///
+ procedure SetActive(ConfigId: Guid; Active: Boolean);
+
+ ///
+ /// Returns whether the feature is currently active for the specified MCP configuration.
+ ///
+ procedure IsActive(ConfigId: Guid): Boolean;
+
+ ///
+ /// Returns whether the feature exposes additional settings (drives the Configure action).
+ ///
+ procedure HasSettings(): Boolean;
+
+ ///
+ /// Opens the feature's settings dialog. No-op when HasSettings() returns false.
+ ///
+ procedure OpenSettings(ConfigId: Guid);
+
+ ///
+ /// Returns the description shown for the feature in the Server Features list.
+ ///
+ procedure Description(): Text[500];
+
+ ///
+ /// Appends the feature's system tools to the buffer. Called only when the feature is active.
+ ///
+ procedure LoadSystemTools(var MCPSystemTool: Record "MCP System Tool");
+
+ ///
+ /// Returns true and the parent feature when this is a sub-feature. The Server Features list shows
+ /// a sub-feature indented beneath its parent.
+ ///
+ procedure TryGetParentFeature(var ParentFeature: Enum "MCP Server Feature"): Boolean;
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIConfigToolLookup.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIConfigToolLookup.Page.al
index 8d55b0acd5..a251c5ee0a 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIConfigToolLookup.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIConfigToolLookup.Page.al
@@ -1,3 +1,4 @@
+#if not CLEAN29
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
@@ -20,6 +21,9 @@ page 8353 "MCP API Config Tool Lookup"
DeleteAllowed = false;
InherentEntitlements = X;
InherentPermissions = X;
+ ObsoleteState = Pending;
+ ObsoleteReason = 'Replaced by page "MCP API Object Lookup", which lists API pages and API queries together.';
+ ObsoleteTag = '29.0';
layout
{
@@ -60,4 +64,5 @@ page 8353 "MCP API Config Tool Lookup"
}
}
}
-}
\ No newline at end of file
+}
+#endif
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIObjectLookup.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIObjectLookup.Page.al
new file mode 100644
index 0000000000..1f9798860d
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIObjectLookup.Page.al
@@ -0,0 +1,75 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+page 8376 "MCP API Object Lookup"
+{
+ PageType = List;
+ ApplicationArea = All;
+ SourceTable = "MCP API Object Buffer";
+ SourceTableTemporary = true;
+ Caption = 'Select APIs';
+ Extensible = false;
+ Editable = false;
+ InsertAllowed = false;
+ ModifyAllowed = false;
+ DeleteAllowed = false;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(Control1)
+ {
+ field("Object Type"; Rec."Object Type")
+ {
+ ToolTip = 'Specifies whether the object is an API page or an API query.';
+ }
+ field("Object ID"; Rec."Object ID")
+ {
+ ToolTip = 'Specifies the object ID.';
+ }
+ field(Name; Rec.Name)
+ {
+ ToolTip = 'Specifies the name of the object.';
+ }
+ field("Entity Name"; Rec."Entity Name")
+ {
+ ToolTip = 'Specifies the entity name of the object.';
+ }
+ field("API Publisher"; Rec."API Publisher")
+ {
+ ToolTip = 'Specifies the API publisher of the object.';
+ }
+ field("API Group"; Rec."API Group")
+ {
+ ToolTip = 'Specifies the API group of the object.';
+ }
+ field("API Version"; Rec."API Version")
+ {
+ ToolTip = 'Specifies the API version of the object.';
+ }
+ }
+ }
+ }
+
+ internal procedure SetObjects(var MCPAPIObjectBuffer: Record "MCP API Object Buffer")
+ begin
+ Rec.Copy(MCPAPIObjectBuffer, true);
+ end;
+
+ internal procedure GetSelectedObjects(var MCPAPIObjectBuffer: Record "MCP API Object Buffer")
+ begin
+ CurrPage.SetSelectionFilter(Rec);
+ if Rec.FindSet() then
+ repeat
+ MCPAPIObjectBuffer := Rec;
+ if MCPAPIObjectBuffer.Insert() then;
+ until Rec.Next() = 0;
+ end;
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIPublisherLookup.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIPublisherLookup.Page.al
index 9b8148a168..87e032058c 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIPublisherLookup.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPAPIPublisherLookup.Page.al
@@ -38,4 +38,4 @@ page 8356 "MCP API Publisher Lookup"
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigCard.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigCard.Page.al
index 8f6583fa94..ad722a4521 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigCard.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigCard.Page.al
@@ -33,6 +33,12 @@ page 8351 "MCP Config Card"
{
ToolTip = 'Specifies the name of the MCP configuration.';
Editable = not IsDefault and not Rec.Active;
+
+ trigger OnValidate()
+ begin
+ if IsNullGuid(Rec.SystemId) then
+ CurrPage.Update();
+ end;
}
field(Description; Rec.Description)
{
@@ -57,43 +63,15 @@ page 8351 "MCP Config Card"
else
if Rec.Default then
Error(DesignatedDefaultCannotBeDeactivatedErr);
+ RefreshSubPages();
+ CurrPage.Update();
end;
}
field(Default; Rec.Default)
{
Caption = 'Default';
- ToolTip = 'Specifies whether this configuration is the default. The default configuration is used when no configuration is specified by a connection. Clear this field to remove the default designation, in which case the system reverts to built-in default configuration.';
- Editable = not IsDefault;
-
- trigger OnValidate()
- begin
- if Rec.Default = xRec.Default then
- exit;
-
- if Rec.Default then
- MCPConfigImplementation.SetAsDefaultConfiguration(Rec.SystemId)
- else
- MCPConfigImplementation.ClearDefaultConfiguration();
- end;
- }
- field(EnableDynamicToolMode; Rec.EnableDynamicToolMode)
- {
- ToolTip = 'Specifies whether to enable dynamic tool mode for this MCP configuration. When enabled, clients can search for tools within the configuration dynamically.';
- Editable = not IsDefault and not Rec.Active;
-
- trigger OnValidate()
- begin
- if not Rec.EnableDynamicToolMode then
- Rec.DiscoverReadOnlyObjects := false;
-
- GetToolModeDescription();
- CurrPage.Update();
- end;
- }
- field(DiscoverReadOnlyObjects; Rec.DiscoverReadOnlyObjects)
- {
- ToolTip = 'Specifies whether to allow discovery of read-only objects not defined in the configuration. Only supported with dynamic tool mode.';
- Editable = not IsDefault and Rec.EnableDynamicToolMode and not Rec.Active;
+ ToolTip = 'Specifies whether this configuration is the default. The default configuration is used when no configuration is specified by a connection. Use the Set as Default and Clear Default actions on the configuration list to change this.';
+ Editable = false;
}
field(AllowProdChanges; Rec.AllowProdChanges)
{
@@ -108,25 +86,12 @@ page 8351 "MCP Config Card"
end;
}
}
- group(ToolModes)
- {
- Caption = 'Tool Modes';
- ShowCaption = false;
-
- field(ToolMode; ToolModeLbl)
- {
- ApplicationArea = All;
- Editable = false;
- Caption = 'Tool Mode';
- ShowCaption = false;
- MultiLine = true;
- }
- }
}
- part(SystemToolList; "MCP System Tool List")
+ part(ServerFeatureList; "MCP Server Feature List")
{
ApplicationArea = All;
- Visible = not IsDefault and Rec.EnableDynamicToolMode;
+ UpdatePropagation = Both;
+ Visible = not IsDefault;
Editable = false;
}
part(ToolList; "MCP Config Tool List")
@@ -134,10 +99,20 @@ page 8351 "MCP Config Card"
ApplicationArea = All;
SubPageLink = ID = field(SystemId);
UpdatePropagation = Both;
- Visible = not IsDefault;
+ Visible = not IsDefault and APIToolsActive;
Editable = not Rec.Active;
}
}
+ area(FactBoxes)
+ {
+ part(SystemToolList; "MCP System Tool List")
+ {
+ ApplicationArea = All;
+ UpdatePropagation = Both;
+ Visible = not IsDefault;
+ Editable = false;
+ }
+ }
}
actions
@@ -175,6 +150,18 @@ page 8351 "MCP Config Card"
Caption = 'Advanced';
Image = Setup;
+ action(ExportConfiguration)
+ {
+ Caption = 'Export';
+ ToolTip = 'Export the selected MCP configuration and its tools to a JSON file.';
+ Image = Export;
+
+ trigger OnAction()
+ begin
+ MCPConfigImplementation.ExportConfigurationToFile(Rec.SystemId, Rec.Name);
+ end;
+ }
+
action(GenerateConnectionString)
{
Caption = 'Connection String';
@@ -197,6 +184,7 @@ page 8351 "MCP Config Card"
Caption = 'Advanced';
actionref(Promoted_GenerateConnectionString; GenerateConnectionString) { }
+ actionref(Promoted_ExportConfiguration; ExportConfiguration) { }
}
}
}
@@ -204,12 +192,11 @@ page 8351 "MCP Config Card"
trigger OnAfterGetRecord()
begin
IsDefault := MCPConfigImplementation.IsDefaultConfiguration(Rec);
- GetToolModeDescription();
end;
- trigger OnNewRecord(BelowxRec: Boolean)
+ trigger OnAfterGetCurrRecord()
begin
- ToolModeLbl := StaticToolModeLbl;
+ RefreshSubPages();
end;
trigger OnDeleteRecord(): Boolean
@@ -230,13 +217,17 @@ page 8351 "MCP Config Card"
var
MCPConfigImplementation: Codeunit "MCP Config Implementation";
IsDefault: Boolean;
- ToolModeLbl: Text;
- StaticToolModeLbl: Label 'In Static Tool Mode, objects in the available tools will be directly exposed to clients. You can manage these tools by adding, modifying, or removing them from the configuration.';
- DynamicToolModeLbl: Label 'In Dynamic Tool Mode, only system tools will be exposed to clients. Objects within the available tools can be discovered, described and invoked dynamically using system tools. You can enable dynamic discovery of any read-only object outside of the available tools using Discover Additional Objects setting.';
+ APIToolsActive: Boolean;
DesignatedDefaultCannotBeDeactivatedErr: Label 'The designated default configuration cannot be deactivated. Clear the default designation first.';
- local procedure GetToolModeDescription(): Text
+ local procedure RefreshSubPages()
+ var
+ ServerFeature: Interface "MCP Server Features";
begin
- ToolModeLbl := Rec.EnableDynamicToolMode ? DynamicToolModeLbl : StaticToolModeLbl;
+ CurrPage.ServerFeatureList.Page.Reload(Rec.SystemId, not IsDefault and not Rec.Active);
+ ServerFeature := "MCP Server Feature"::"API Tools";
+ APIToolsActive := ServerFeature.IsActive(Rec.SystemId);
+ CurrPage.SystemToolList.Page.Reload(Rec.SystemId);
+ CurrPage.ToolList.Page.SetConfigActive(Rec.Active);
end;
-}
\ No newline at end of file
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigList.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigList.Page.al
index ac86b82476..dfa7c44f8e 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigList.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigList.Page.al
@@ -45,13 +45,17 @@ page 8350 "MCP Config List"
ToolTip = 'Specifies whether this configuration is the default. The default configuration is used when no configuration is specified by a connection. Clear this field to remove the default designation, in which case the system reverts to built-in default configuration.';
Editable = false;
}
- field(EnableDynamicToolMode; Rec.EnableDynamicToolMode)
+ field(APITools; Rec.EnableApiTools)
{
- ToolTip = 'Specifies whether to enable dynamic tool mode for this MCP configuration. When enabled, clients can search for tools within the configuration dynamically.';
+ Caption = 'API Tools';
+ ToolTip = 'Specifies whether the API Tools feature is enabled for this configuration.';
+ Editable = false;
}
- field(DiscoverReadOnlyObjects; Rec.DiscoverReadOnlyObjects)
+ field(DataQueryTools; Rec.EnableAlQueryTools)
{
- ToolTip = 'Specifies whether to allow discovery of read-only objects not defined in the configuration. Only supported with dynamic tool mode.';
+ Caption = 'Data Query Tools';
+ ToolTip = 'Specifies whether the Data Query Tools feature is enabled for this configuration.';
+ Editable = false;
}
}
}
@@ -77,6 +81,40 @@ page 8350 "MCP Config List"
}
area(Processing)
{
+ action(SetAsDefault)
+ {
+ Caption = 'Set as Default';
+ ToolTip = 'Set this configuration as the default. It will be used when no configuration is specified by a connection.';
+ Image = Approve;
+ AccessByPermission = tabledata "MCP Configuration" = M;
+ Scope = Repeater;
+ Enabled = not Rec.Default;
+
+ trigger OnAction()
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ MCPConfigImplementation.SetAsDefaultConfiguration(Rec.SystemId);
+ CurrPage.Update(false);
+ end;
+ }
+ action(ClearDefault)
+ {
+ Caption = 'Clear Default';
+ ToolTip = 'Remove the default designation from this configuration. The system will revert to built-in default settings.';
+ Image = Undo;
+ AccessByPermission = tabledata "MCP Configuration" = M;
+ Scope = Repeater;
+ Enabled = Rec.Default;
+
+ trigger OnAction()
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ MCPConfigImplementation.ClearDefaultConfiguration();
+ CurrPage.Update(false);
+ end;
+ }
action(GiveFeedback)
{
Caption = 'Give Feedback';
@@ -142,15 +180,17 @@ page 8350 "MCP Config List"
area(Promoted)
{
actionref(Promoted_Copy; Copy) { }
+ actionref(Promoted_SetAsDefault; SetAsDefault) { }
+ actionref(Promoted_ClearDefault; ClearDefault) { }
actionref(Promoted_GiveFeedback; GiveFeedback) { }
+ actionref(Promoted_ExportConfiguration; ExportConfiguration) { }
+ actionref(Promoted_ImportConfiguration; ImportConfiguration) { }
group(Promoted_Advanced)
{
Caption = 'Advanced';
actionref(Promoted_GenerateConnectionString; GenerateConnectionString) { }
actionref(Promoted_MCPEntraApplications; MCPEntraApplications) { }
- actionref(Promoted_ExportConfiguration; ExportConfiguration) { }
- actionref(Promoted_ImportConfiguration; ImportConfiguration) { }
}
}
}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigToolList.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigToolList.Page.al
index 9361e56172..57d3e12e44 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigToolList.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPConfigToolList.Page.al
@@ -9,7 +9,7 @@ using System.Reflection;
page 8352 "MCP Config Tool List"
{
- Caption = 'Available Tools';
+ Caption = 'Available APIs';
ApplicationArea = All;
PageType = ListPart;
SourceTable = "MCP Configuration Tool";
@@ -26,49 +26,18 @@ page 8352 "MCP Config Tool List"
repeater(Control1)
{
ShowCaption = false;
+ field("Object Type"; Rec."Object Type")
+ {
+ ToolTip = 'Specifies the type of the object.';
+ }
field("Object Id"; Rec."Object Id")
{
ToolTip = 'Specifies the ID of the object.';
trigger OnLookup(var Text: Text): Boolean
- var
- PageMetadata: Record "Page Metadata";
- QueryMetadata: Record "Query Metadata";
begin
- case Rec."Object Type" of
- Rec."Object Type"::Page:
- begin
- if not MCPConfigImplementation.LookupAPIPageTools(PageMetadata) then
- exit;
-
- if not PageMetadata.FindSet() then
- exit;
-
- repeat
- if MCPConfigImplementation.CheckAPIToolExists(Rec.ID, PageMetadata.ID, Rec."Object Type") then
- continue;
- MCPConfig.CreateAPITool(Rec.ID, PageMetadata.ID);
- until PageMetadata.Next() = 0;
- end;
- Rec."Object Type"::Query:
- begin
- if not MCPConfigImplementation.LookupAPIQueryTools(QueryMetadata) then
- exit;
-
- if not QueryMetadata.FindSet() then
- exit;
-
- repeat
- if MCPConfigImplementation.CheckAPIToolExists(Rec.ID, QueryMetadata.ID, Rec."Object Type") then
- continue;
- MCPConfig.CreateQueryAPITool(Rec.ID, QueryMetadata.ID);
- until QueryMetadata.Next() = 0;
- end;
- end;
-
- if not IsNullGuid(Rec.SystemId) then
- Rec.Delete();
- CurrPage.Update();
+ // Routes through the same unified (pages + queries) lookup as the Select APIs action.
+ AddAPIObjects();
end;
trigger OnValidate()
@@ -99,36 +68,32 @@ page 8352 "MCP Config Tool List"
}
field("Allow Read"; Rec."Allow Read")
{
- ToolTip = 'Specifies whether read operations are allowed for this tool.';
+ ToolTip = 'Specifies whether read operations are allowed for this API.';
}
field("Allow Create"; Rec."Allow Create")
{
- ToolTip = 'Specifies whether create operations are allowed for this tool.';
+ ToolTip = 'Specifies whether create operations are allowed for this API.';
Editable = AllowCreateEditable and AllowCreateUpdateDeleteTools and (Rec."Object Type" = Rec."Object Type"::Page);
}
field("Allow Modify"; Rec."Allow Modify")
{
- ToolTip = 'Specifies whether modify operations are allowed for this tool.';
+ ToolTip = 'Specifies whether modify operations are allowed for this API.';
Editable = AllowModifyEditable and AllowCreateUpdateDeleteTools and (Rec."Object Type" = Rec."Object Type"::Page);
}
field("Allow Delete"; Rec."Allow Delete")
{
- ToolTip = 'Specifies whether delete operations are allowed for this tool.';
+ ToolTip = 'Specifies whether delete operations are allowed for this API.';
Editable = AllowDeleteEditable and AllowCreateUpdateDeleteTools and (Rec."Object Type" = Rec."Object Type"::Page);
}
field("Allow Bound Actions"; Rec."Allow Bound Actions")
{
- ToolTip = 'Specifies whether bound actions are allowed for this tool.';
+ ToolTip = 'Specifies whether bound actions are allowed for this API.';
Editable = AllowCreateUpdateDeleteTools and (Rec."Object Type" = Rec."Object Type"::Page);
}
- field("Object Type"; Rec."Object Type")
- {
- ToolTip = 'Specifies the type of the object.';
- }
field("API Version"; Rec."API Version")
{
Caption = 'API Version';
- ToolTip = 'Specifies the API version of the tool.';
+ ToolTip = 'Specifies the API version.';
trigger OnLookup(var Text: Text): Boolean
var
@@ -165,59 +130,24 @@ page 8352 "MCP Config Tool List"
{
area(Processing)
{
- action(SelectTools)
+ action(SelectAPIs)
{
- Caption = 'Select Tools';
+ Caption = 'Select APIs';
Ellipsis = true;
Image = Resource;
- ToolTip = 'Opens a lookup to select API tools to add to this configuration.';
+ ToolTip = 'Opens a lookup to select API objects to add to this configuration.';
+ Enabled = not IsConfigActive;
trigger OnAction()
- var
- PageMetadata: Record "Page Metadata";
- QueryMetadata: Record "Query Metadata";
begin
- case Rec."Object Type" of
- Rec."Object Type"::Page:
- begin
- if not MCPConfigImplementation.LookupAPIPageTools(PageMetadata) then
- exit;
-
- if not PageMetadata.FindSet() then
- exit;
-
- repeat
- if MCPConfigImplementation.CheckAPIToolExists(Rec.ID, PageMetadata.ID, Rec."Object Type") then
- continue;
- MCPConfig.CreateAPITool(Rec.ID, PageMetadata.ID);
- until PageMetadata.Next() = 0;
- end;
- Rec."Object Type"::Query:
- begin
- if not MCPConfigImplementation.LookupAPIQueryTools(QueryMetadata) then
- exit;
-
- if not QueryMetadata.FindSet() then
- exit;
-
- repeat
- if MCPConfigImplementation.CheckAPIToolExists(Rec.ID, QueryMetadata.ID, Rec."Object Type") then
- continue;
- MCPConfig.CreateQueryAPITool(Rec.ID, QueryMetadata.ID);
- until QueryMetadata.Next() = 0;
- end;
- end;
-
- if not IsNullGuid(Rec.SystemId) then
- Rec.Delete();
- CurrPage.Update();
+ AddAPIObjects();
end;
}
- action(AddToolsByAPIGroup)
+ action(AddAPIsByAPIGroup)
{
- Caption = 'Add Tools by API Group';
+ Caption = 'Add APIs by API Group';
Image = NewResourceGroup;
- ToolTip = 'Adds tools to the configuration by API publisher and group.';
+ ToolTip = 'Adds APIs to the configuration by API publisher and group.';
Enabled = not IsConfigActive;
trigger OnAction()
@@ -228,9 +158,9 @@ page 8352 "MCP Config Tool List"
}
action(AddStandardAPITools)
{
- Caption = 'Add All Standard APIs as Tools';
+ Caption = 'Add All Standard APIs';
Image = ResourceGroup;
- ToolTip = 'Adds tools for all standard API pages and queries to the configuration.';
+ ToolTip = 'Adds all standard API pages and queries to the configuration.';
Enabled = not IsConfigActive;
trigger OnAction()
@@ -260,6 +190,11 @@ page 8352 "MCP Config Tool List"
IsConfigActive := MCPConfigImplementation.IsConfigurationActive(Rec.ID);
end;
+ internal procedure SetConfigActive(IsActive: Boolean)
+ begin
+ IsConfigActive := IsActive;
+ end;
+
trigger OnNewRecord(BelowxRec: Boolean)
begin
Rec."Allow Read" := true;
@@ -293,4 +228,28 @@ page 8352 "MCP Config Tool List"
if MCPConfiguration.GetBySystemId(Rec.ID) then
AllowCreateUpdateDeleteTools := MCPConfiguration.AllowProdChanges;
end;
-}
\ No newline at end of file
+
+ local procedure AddAPIObjects()
+ var
+ TempSelectedObjects: Record "MCP API Object Buffer";
+ begin
+ if not MCPConfigImplementation.LookupAPIObjects(TempSelectedObjects) then
+ exit;
+
+ if TempSelectedObjects.FindSet() then
+ repeat
+ case TempSelectedObjects."Object Type" of
+ TempSelectedObjects."Object Type"::Page:
+ if not MCPConfigImplementation.CheckAPIToolExists(Rec.ID, TempSelectedObjects."Object ID", Rec."Object Type"::Page) then
+ MCPConfig.CreateAPITool(Rec.ID, TempSelectedObjects."Object ID");
+ TempSelectedObjects."Object Type"::Query:
+ if not MCPConfigImplementation.CheckAPIToolExists(Rec.ID, TempSelectedObjects."Object ID", Rec."Object Type"::Query) then
+ MCPConfig.CreateQueryAPITool(Rec.ID, TempSelectedObjects."Object ID");
+ end;
+ until TempSelectedObjects.Next() = 0;
+
+ if not IsNullGuid(Rec.SystemId) then
+ Rec.Delete();
+ CurrPage.Update();
+ end;
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPQueryConfigToolLookup.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPQueryConfigToolLookup.Page.al
index de7c71e620..6b74dcf7ad 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPQueryConfigToolLookup.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPQueryConfigToolLookup.Page.al
@@ -1,3 +1,4 @@
+#if not CLEAN29
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
@@ -20,6 +21,9 @@ page 8367 "MCP Query Config Tool Lookup"
DeleteAllowed = false;
InherentEntitlements = X;
InherentPermissions = X;
+ ObsoleteState = Pending;
+ ObsoleteReason = 'Replaced by page "MCP API Object Lookup", which lists API pages and API queries together.';
+ ObsoleteTag = '29.0';
layout
{
@@ -60,4 +64,5 @@ page 8367 "MCP Query Config Tool Lookup"
}
}
}
-}
\ No newline at end of file
+}
+#endif
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureList.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureList.Page.al
new file mode 100644
index 0000000000..51b1b17897
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureList.Page.al
@@ -0,0 +1,180 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+page 8368 "MCP Server Feature List"
+{
+ Caption = 'Server Features';
+ ApplicationArea = All;
+ PageType = ListPart;
+ SourceTable = "MCP Server Feature";
+ SourceTableTemporary = true;
+ Editable = false;
+ InsertAllowed = false;
+ ModifyAllowed = false;
+ DeleteAllowed = false;
+ Extensible = false;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(Features)
+ {
+ ShowCaption = false;
+ IndentationColumn = Rec.Indentation;
+ IndentationControls = Feature;
+ field(Feature; Rec.Feature)
+ {
+ Caption = 'Name';
+ ToolTip = 'Specifies the name of the server feature.';
+ Width = 10;
+ }
+ field("Description"; Rec."Description")
+ {
+ ToolTip = 'Specifies the description of the server feature.';
+ }
+ field("Status"; Rec."Status")
+ {
+ ToolTip = 'Specifies whether the server feature is active or inactive.';
+ StyleExpr = StatusStyleExpr;
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Processing)
+ {
+ action(Activate)
+ {
+ Caption = 'Activate';
+ ToolTip = 'Activate the selected server feature for this MCP configuration.';
+ Image = Start;
+ Visible = ActionsEnabled and (Rec.Status = Rec.Status::Inactive);
+ Scope = Repeater;
+
+ trigger OnAction()
+ begin
+ SetActive(true);
+ end;
+ }
+ action(Deactivate)
+ {
+ Caption = 'Deactivate';
+ ToolTip = 'Deactivate the selected server feature for this MCP configuration.';
+ Image = Stop;
+ Visible = ActionsEnabled and (Rec.Status = Rec.Status::Active);
+ Scope = Repeater;
+
+ trigger OnAction()
+ begin
+ SetActive(false);
+ end;
+ }
+ action(Configure)
+ {
+ Caption = 'Configure';
+ Ellipsis = true;
+ ToolTip = 'Open feature-specific settings for the selected server feature.';
+ Image = Setup;
+ Enabled = ActionsEnabled and (Rec.Status = Rec.Status::Active) and Rec.Configurable;
+ Scope = Repeater;
+
+ trigger OnAction()
+ begin
+ OpenSettings();
+ end;
+ }
+ }
+ }
+
+ trigger OnAfterGetRecord()
+ begin
+ SetStatusStyle();
+ end;
+
+ trigger OnAfterGetCurrRecord()
+ begin
+ SetStatusStyle();
+ end;
+
+ var
+ ParentSystemId: Guid;
+ ActionsEnabled: Boolean;
+ StatusStyleExpr: Text;
+
+ internal procedure Reload(ConfigSystemId: Guid; CanModify: Boolean)
+ var
+ ServerFeature: Enum "MCP Server Feature";
+ FeatureImplementations: List of [Integer];
+ FeatureImplementation: Integer;
+ begin
+ ParentSystemId := ConfigSystemId;
+ ActionsEnabled := CanModify;
+ Rec.Reset();
+ Rec.DeleteAll();
+ FeatureImplementations := ServerFeature.Ordinals();
+ foreach FeatureImplementation in FeatureImplementations do begin
+ ServerFeature := "MCP Server Feature".FromInteger(FeatureImplementation);
+ InsertRow(ServerFeature);
+ end;
+ if Rec.FindFirst() then;
+ end;
+
+ local procedure InsertRow(NewFeature: Enum "MCP Server Feature")
+ var
+ ServerFeature: Interface "MCP Server Features";
+ ParentFeature: Enum "MCP Server Feature";
+ Indentation: Integer;
+ begin
+ ServerFeature := NewFeature;
+ if ServerFeature.TryGetParentFeature(ParentFeature) then
+ Indentation := 1; // sub-feature: always shown, indented beneath its parent
+ Rec.Init();
+ Rec.Feature := NewFeature;
+ Rec.Description := ServerFeature.Description();
+ Rec.Indentation := Indentation;
+ if ServerFeature.IsActive(ParentSystemId) then
+ Rec.Status := Rec.Status::Active
+ else
+ Rec.Status := Rec.Status::Inactive;
+ Rec.Configurable := ServerFeature.HasSettings();
+ Rec.Insert();
+ end;
+
+ local procedure SetActive(NewActive: Boolean)
+ var
+ ServerFeature: Interface "MCP Server Features";
+ begin
+ ServerFeature := Rec.Feature;
+ ServerFeature.SetActive(ParentSystemId, NewActive);
+
+ // Reload so a cascaded change to another feature (disabling API Tools turns off
+ // Dynamic Tool Mode) is reflected on every row, not just the one toggled.
+ Reload(ParentSystemId, ActionsEnabled);
+ CurrPage.Update(false);
+ end;
+
+ local procedure SetStatusStyle()
+ begin
+ if Rec.Status = Rec.Status::Active then
+ StatusStyleExpr := 'Favorable'
+ else
+ StatusStyleExpr := '';
+ end;
+
+ local procedure OpenSettings()
+ var
+ ServerFeature: Interface "MCP Server Features";
+ begin
+ ServerFeature := Rec.Feature;
+ ServerFeature.OpenSettings(ParentSystemId);
+ end;
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureSettings.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureSettings.Page.al
new file mode 100644
index 0000000000..0987310531
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPServerFeatureSettings.Page.al
@@ -0,0 +1,78 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+page 8369 "MCP Server Feature Settings"
+{
+ Caption = 'Server Feature Settings';
+ PageType = StandardDialog;
+ ApplicationArea = All;
+ Extensible = false;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ layout
+ {
+ area(Content)
+ {
+ group(Settings)
+ {
+ ShowCaption = false;
+
+ field(DiscoverReadOnlyObjects; DiscoverReadOnlyObjectsLocal)
+ {
+ Caption = 'Discover Read-Only Objects';
+ ToolTip = 'Specifies whether to allow discovery of read-only objects not defined in the configuration.';
+ Visible = Feature = Feature::"Dynamic Tool Mode";
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Processing)
+ {
+ }
+ }
+
+ trigger OnOpenPage()
+ var
+ ParentConfig: Record "MCP Configuration";
+ begin
+ if ParentConfig.GetBySystemId(ConfigSystemId) then
+ DiscoverReadOnlyObjectsLocal := ParentConfig.DiscoverReadOnlyObjects;
+ UpdateCaption();
+ end;
+
+ var
+ ConfigSystemId: Guid;
+ Feature: Enum "MCP Server Feature";
+ DiscoverReadOnlyObjectsLocal: Boolean;
+
+ internal procedure SetContext(NewConfigSystemId: Guid; NewFeature: Enum "MCP Server Feature")
+ begin
+ ConfigSystemId := NewConfigSystemId;
+ Feature := NewFeature;
+ end;
+
+ internal procedure SaveChanges()
+ var
+ MCPConfigImplementation: Codeunit "MCP Config Implementation";
+ begin
+ case Feature of
+ Feature::"Dynamic Tool Mode":
+ MCPConfigImplementation.EnableDiscoverReadOnlyObjects(ConfigSystemId, DiscoverReadOnlyObjectsLocal);
+ end;
+ end;
+
+ local procedure UpdateCaption()
+ var
+ SettingsForLbl: Label '%1 Settings', Comment = '%1 = the name of the server feature';
+ begin
+ CurrPage.Caption(StrSubstNo(SettingsForLbl, Format(Feature)));
+ end;
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPSystemToolList.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPSystemToolList.Page.al
index 55344181c5..5f6610d0ec 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPSystemToolList.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPSystemToolList.Page.al
@@ -12,6 +12,7 @@ page 8365 "MCP System Tool List"
PageType = ListPart;
SourceTable = "MCP System Tool";
SourceTableTemporary = true;
+ SourceTableView = sorting("Server Feature", "Tool Name");
Editable = false;
InsertAllowed = false;
ModifyAllowed = false;
@@ -33,21 +34,21 @@ page 8365 "MCP System Tool List"
}
}
- trigger OnOpenPage()
- begin
- LoadSystemTools();
- end;
-
+ internal procedure Reload(ConfigSystemId: Guid)
var
- MCPConfigImplementation: Codeunit "MCP Config Implementation";
- IsLoaded: Boolean;
-
- local procedure LoadSystemTools()
+ ServerFeature: Interface "MCP Server Features";
+ ServerFeatureEnum: Enum "MCP Server Feature";
+ FeatureImplementations: List of [Integer];
+ FeatureImplementation: Integer;
begin
- if IsLoaded then
- exit;
-
- IsLoaded := true;
- MCPConfigImplementation.LoadSystemTools(Rec);
+ Rec.Reset();
+ Rec.DeleteAll();
+ FeatureImplementations := ServerFeatureEnum.Ordinals();
+ foreach FeatureImplementation in FeatureImplementations do begin
+ ServerFeature := "MCP Server Feature".FromInteger(FeatureImplementation);
+ if ServerFeature.IsActive(ConfigSystemId) then
+ ServerFeature.LoadSystemTools(Rec);
+ end;
+ if Rec.FindFirst() then;
end;
-}
\ No newline at end of file
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Pages/MCPToolsByAPIGroup.Page.al b/src/System Application/App/MCP/src/Configuration/Pages/MCPToolsByAPIGroup.Page.al
index 9c24727339..41877d58d6 100644
--- a/src/System Application/App/MCP/src/Configuration/Pages/MCPToolsByAPIGroup.Page.al
+++ b/src/System Application/App/MCP/src/Configuration/Pages/MCPToolsByAPIGroup.Page.al
@@ -75,4 +75,4 @@ page 8354 "MCP Tools By API Group"
begin
exit(APIPublisher);
end;
-}
\ No newline at end of file
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Tables/MCPAPIObjectBuffer.Table.al b/src/System Application/App/MCP/src/Configuration/Tables/MCPAPIObjectBuffer.Table.al
new file mode 100644
index 0000000000..5cb0463c02
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Tables/MCPAPIObjectBuffer.Table.al
@@ -0,0 +1,58 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+// Temporary buffer that unifies API pages and API queries into a single list so the "Select APIs"
+// lookup can show both in one page. Populated by MCPConfigImplementation.LookupAPIObjects; never
+// persisted (used as temporary only).
+table 8357 "MCP API Object Buffer"
+{
+ Access = Internal;
+ DataClassification = SystemMetadata;
+ TableType = Temporary;
+
+ fields
+ {
+ field(1; "Object Type"; Option)
+ {
+ Caption = 'Object Type';
+ OptionMembers = Page,Query;
+ OptionCaption = 'Page,Query';
+ }
+ field(2; "Object ID"; Integer)
+ {
+ Caption = 'ID';
+ }
+ field(3; "Name"; Text[250])
+ {
+ Caption = 'Name';
+ }
+ field(4; "Entity Name"; Text[250])
+ {
+ Caption = 'Entity Name';
+ }
+ field(5; "API Publisher"; Text[250])
+ {
+ Caption = 'API Publisher';
+ }
+ field(6; "API Group"; Text[250])
+ {
+ Caption = 'API Group';
+ }
+ field(7; "API Version"; Text[250])
+ {
+ Caption = 'API Version';
+ }
+ }
+
+ keys
+ {
+ key(PK; "Object Type", "Object ID")
+ {
+ Clustered = true;
+ }
+ }
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Tables/MCPServerFeature.Table.al b/src/System Application/App/MCP/src/Configuration/Tables/MCPServerFeature.Table.al
new file mode 100644
index 0000000000..c065e81952
--- /dev/null
+++ b/src/System Application/App/MCP/src/Configuration/Tables/MCPServerFeature.Table.al
@@ -0,0 +1,50 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+table 8355 "MCP Server Feature"
+{
+ Access = Internal;
+ DataClassification = SystemMetadata;
+ TableType = Temporary;
+
+ fields
+ {
+ field(1; Feature; Enum "MCP Server Feature")
+ {
+ Caption = 'Feature';
+ }
+ field(2; "Description"; Text[500])
+ {
+ Caption = 'Description';
+ ToolTip = 'Specifies what enabling this server feature does.';
+ }
+ field(3; "Status"; Option)
+ {
+ Caption = 'Status';
+ OptionMembers = Inactive,Active;
+ OptionCaption = 'Inactive,Active';
+ ToolTip = 'Specifies whether this server feature is currently active for the configuration.';
+ }
+ field(4; Configurable; Boolean)
+ {
+ Caption = 'Configurable';
+ ToolTip = 'Specifies whether this server feature exposes additional settings.';
+ }
+ field(5; Indentation; Integer)
+ {
+ Caption = 'Indentation';
+ }
+ }
+
+ keys
+ {
+ key(Key1; Feature)
+ {
+ Clustered = true;
+ }
+ }
+}
diff --git a/src/System Application/App/MCP/src/Configuration/Tables/MCPSystemTool.Table.al b/src/System Application/App/MCP/src/Configuration/Tables/MCPSystemTool.Table.al
index e056179a88..6f621e2936 100644
--- a/src/System Application/App/MCP/src/Configuration/Tables/MCPSystemTool.Table.al
+++ b/src/System Application/App/MCP/src/Configuration/Tables/MCPSystemTool.Table.al
@@ -23,11 +23,16 @@ table 8353 "MCP System Tool"
Caption = 'Tool Description';
ToolTip = 'Specifies the description of the system tool.';
}
+ field(3; "Server Feature"; Enum "MCP Server Feature")
+ {
+ Caption = 'Server Feature';
+ ToolTip = 'Specifies which server feature exposes this tool.';
+ }
}
keys
{
- key(Key1; "Tool Name")
+ key(Key1; "Server Feature", "Tool Name")
{
Clustered = true;
}
@@ -36,4 +41,4 @@ table 8353 "MCP System Tool"
fieldgroups
{
}
-}
\ No newline at end of file
+}
diff --git a/src/System Application/Test Library/MCP/src/MCPConfigTestLibrary.Codeunit.al b/src/System Application/Test Library/MCP/src/MCPConfigTestLibrary.Codeunit.al
index dc02be1d08..01126a6f98 100644
--- a/src/System Application/Test Library/MCP/src/MCPConfigTestLibrary.Codeunit.al
+++ b/src/System Application/Test Library/MCP/src/MCPConfigTestLibrary.Codeunit.al
@@ -13,9 +13,11 @@ codeunit 130131 "MCP Config Test Library"
var
MCPConfigImplementation: Codeunit "MCP Config Implementation";
- procedure LookupAPIPageTools(var PageMetadata: Record "Page Metadata"): Boolean
+ procedure LookupAPIObjects(): Boolean
+ var
+ TempMCPAPIObjectBuffer: Record "MCP API Object Buffer";
begin
- exit(MCPConfigImplementation.LookupAPIPageTools(PageMetadata));
+ exit(MCPConfigImplementation.LookupAPIObjects(TempMCPAPIObjectBuffer));
end;
procedure AddToolsByAPIGroup(ConfigId: Guid)
@@ -28,11 +30,6 @@ codeunit 130131 "MCP Config Test Library"
MCPConfigImplementation.AddStandardAPITools(ConfigId);
end;
- procedure LookupAPIQueryTools(var QueryMetadata: Record "Query Metadata"): Boolean
- begin
- exit(MCPConfigImplementation.LookupAPIQueryTools(QueryMetadata));
- end;
-
procedure LookupAPIPublisher(var APIPublisher: Text; var APIGroup: Text)
var
TempMCPAPIPublisherGroup: Record "MCP API Publisher Group";
diff --git a/src/System Application/Test/MCP/src/MCPConfigTest.Codeunit.al b/src/System Application/Test/MCP/src/MCPConfigTest.Codeunit.al
index bf2d0f070f..f36d12d322 100644
--- a/src/System Application/Test/MCP/src/MCPConfigTest.Codeunit.al
+++ b/src/System Application/Test/MCP/src/MCPConfigTest.Codeunit.al
@@ -86,8 +86,9 @@ codeunit 130130 "MCP Config Test"
MCPConfiguration: Record "MCP Configuration";
ConfigId: Guid;
begin
- // [GIVEN] Configuration is created
+ // [GIVEN] Configuration is created with API Tools enabled (Dynamic Tool Mode requires it)
ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableAPITools(ConfigId, true);
// [WHEN] Enable tool search mode is called
MCPConfig.EnableDynamicToolMode(ConfigId, true);
@@ -251,6 +252,82 @@ codeunit 130130 "MCP Config Test"
#endregion
+ #region API Tools
+
+ [Test]
+ procedure TestEnableAPITools()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ ConfigId: Guid;
+ begin
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableAPITools(ConfigId, true);
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsTrue(MCPConfiguration.EnableApiTools, 'API Tools should be enabled');
+ end;
+
+ [Test]
+ procedure TestDisableAPITools()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ ConfigId: Guid;
+ begin
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableAPITools(ConfigId, false);
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsFalse(MCPConfiguration.EnableApiTools, 'API Tools should be disabled');
+ end;
+
+ [Test]
+ procedure TestDisableAPIToolsDisablesDynamicToolMode()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ ConfigId: Guid;
+ begin
+ // [GIVEN] A configuration with API Tools, Dynamic Tool Mode, and Discover Read-Only Objects on
+ ConfigId := CreateMCPConfig(false, true, true, true);
+ MCPConfig.EnableAPITools(ConfigId, true);
+
+ // [WHEN] API Tools is disabled
+ MCPConfig.EnableAPITools(ConfigId, false);
+
+ // [THEN] Dynamic Tool Mode and Discover Read-Only Objects cascade off with it
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsFalse(MCPConfiguration.EnableApiTools, 'API Tools should be disabled');
+ Assert.IsFalse(MCPConfiguration.EnableDynamicToolMode, 'Dynamic Tool Mode should cascade off');
+ Assert.IsFalse(MCPConfiguration.DiscoverReadOnlyObjects, 'Discover Read-Only Objects should cascade off');
+ end;
+
+ #endregion
+
+ #region Data Query Tools
+
+ [Test]
+ procedure TestEnableDataQueryTools()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ ConfigId: Guid;
+ begin
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableDataQueryTools(ConfigId, true);
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsTrue(MCPConfiguration.EnableAlQueryTools, 'Data Query Tools should be enabled');
+ end;
+
+ [Test]
+ procedure TestDisableDataQueryTools()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ ConfigId: Guid;
+ begin
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableDataQueryTools(ConfigId, false);
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsFalse(MCPConfiguration.EnableAlQueryTools, 'Data Query Tools should be disabled');
+ end;
+
+ #endregion
+
#region Tools
[Test]
@@ -429,39 +506,13 @@ codeunit 130130 "MCP Config Test"
end;
[Test]
- [HandlerFunctions('LookupAPIPageToolsOKHandler')]
- procedure TestLookupAPIPageTools()
- var
- PageMetadata: Record "Page Metadata";
- Result: Boolean;
- begin
- // [GIVEN] No preselected page
-
- // [WHEN] Lookup API tools is called and a page is selected
- Result := MCPConfigTestLibrary.LookupAPIPageTools(PageMetadata);
-
- // [THEN] Correct page is selected
- Assert.IsTrue(Result, 'Result is not true');
- PageMetadata.FindFirst();
- Assert.AreEqual(Page::"Mock API", PageMetadata.ID, 'PageId mismatch');
- end;
-
- [Test]
- [HandlerFunctions('LookupAPIQueryToolsOKHandler')]
- procedure TestLookupAPIQueryTools()
- var
- QueryMetadata: Record "Query Metadata";
- Result: Boolean;
+ [HandlerFunctions('LookupAPIObjectsOKHandler')]
+ procedure TestLookupAPIObjects()
begin
- // [GIVEN] No preselected query
-
- // [WHEN] Lookup API query tools is called and a query is selected
- Result := MCPConfigTestLibrary.LookupAPIQueryTools(QueryMetadata);
-
- // [THEN] Correct query is selected
- Assert.IsTrue(Result, 'Result is not true');
- QueryMetadata.FindFirst();
- Assert.AreEqual(Query::"Mock API Query", QueryMetadata.ID, 'QueryId mismatch');
+ // [GIVEN] No preselected object
+ // [WHEN] The unified API lookup (pages + queries) is opened and an object is selected
+ // [THEN] A selection is returned
+ Assert.IsTrue(MCPConfigTestLibrary.LookupAPIObjects(), 'The lookup did not return a selection');
end;
[Test]
@@ -928,11 +979,14 @@ codeunit 130130 "MCP Config Test"
ConfigJson: JsonObject;
JsonToken: JsonToken;
begin
- // [GIVEN] Configuration with two tools is created
+ // [GIVEN] Configuration with two tools and both tool features enabled is created
ConfigId := CreateMCPConfig(false, true, true, true);
CreateMCPConfigTool(ConfigId);
CreateMCPConfigTool(ConfigId);
MCPConfiguration.GetBySystemId(ConfigId);
+ MCPConfiguration.EnableApiTools := true;
+ MCPConfiguration.EnableAlQueryTools := true;
+ MCPConfiguration.Modify();
// [WHEN] Export configuration is called
TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8);
@@ -949,6 +1003,12 @@ codeunit 130130 "MCP Config Test"
ConfigJson.Get('enableDynamicToolMode', JsonToken);
Assert.AreEqual(true, JsonToken.AsValue().AsBoolean(), 'EnableDynamicToolMode mismatch');
+ ConfigJson.Get('enableApiTools', JsonToken);
+ Assert.AreEqual(true, JsonToken.AsValue().AsBoolean(), 'EnableApiTools mismatch');
+
+ ConfigJson.Get('enableAlQueryTools', JsonToken);
+ Assert.AreEqual(true, JsonToken.AsValue().AsBoolean(), 'EnableAlQueryTools mismatch');
+
ConfigJson.Get('tools', JsonToken);
Assert.AreEqual(2, JsonToken.AsArray().Count(), 'Tools count mismatch');
end;
@@ -966,10 +1026,14 @@ codeunit 130130 "MCP Config Test"
NewName: Text[100];
NewDescription: Text[250];
begin
- // [GIVEN] Configuration with two tools is created and exported
+ // [GIVEN] Configuration with two tools and both tool features enabled is created and exported
SourceConfigId := CreateMCPConfig(false, true, true, true);
CreateMCPConfigTool(SourceConfigId);
CreateMCPConfigTool(SourceConfigId);
+ MCPConfiguration.GetBySystemId(SourceConfigId);
+ MCPConfiguration.EnableApiTools := true;
+ MCPConfiguration.EnableAlQueryTools := true;
+ MCPConfiguration.Modify();
TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8);
MCPConfig.ExportConfiguration(SourceConfigId, OutStream);
@@ -987,6 +1051,8 @@ codeunit 130130 "MCP Config Test"
Assert.IsFalse(MCPConfiguration.Active, 'Imported config should be inactive');
Assert.IsTrue(MCPConfiguration.EnableDynamicToolMode, 'EnableDynamicToolMode mismatch');
Assert.IsTrue(MCPConfiguration.DiscoverReadOnlyObjects, 'DiscoverReadOnlyObjects mismatch');
+ Assert.IsTrue(MCPConfiguration.EnableApiTools, 'EnableApiTools mismatch');
+ Assert.IsTrue(MCPConfiguration.EnableAlQueryTools, 'EnableAlQueryTools mismatch');
// [THEN] Tools are imported with correct API version
MCPConfigurationTool.SetRange(ID, ImportedConfigId);
@@ -1077,8 +1143,6 @@ codeunit 130130 "MCP Config Test"
Assert.IsFalse(MCPConfigCard.Name.Editable(), 'Name field is editable');
Assert.IsFalse(MCPConfigCard.Description.Editable(), 'Description field is editable');
Assert.IsFalse(MCPConfigCard.Active.Editable(), 'Active field is editable');
- Assert.IsFalse(MCPConfigCard.EnableDynamicToolMode.Editable(), 'EnableDynamicToolMode field is editable');
- Assert.IsFalse(MCPConfigCard.DiscoverReadOnlyObjects.Editable(), 'DiscoverReadOnlyObjects field is editable');
Assert.IsFalse(MCPConfigCard.ToolList.Visible(), 'ToolList is visible');
end;
@@ -1227,6 +1291,105 @@ codeunit 130130 "MCP Config Test"
#endregion
+ #region Server Features
+
+ [Test]
+ procedure TestServerFeaturesListShowsAllFeatures()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ MCPConfigCard: TestPage "MCP Config Card";
+ ConfigId: Guid;
+ begin
+ // [GIVEN] A non-default configuration
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfiguration.GetBySystemId(ConfigId);
+
+ // [WHEN] The configuration card is opened
+ MCPConfigCard.OpenEdit();
+ MCPConfigCard.GoToRecord(MCPConfiguration);
+
+ // [THEN] The Server Features list shows all three features in enum order
+ Assert.IsTrue(MCPConfigCard.ServerFeatureList.First(), 'Server Features list is empty');
+ Assert.AreEqual('API Tools', MCPConfigCard.ServerFeatureList.Feature.Value, 'Unexpected first feature');
+ Assert.IsTrue(MCPConfigCard.ServerFeatureList.Next(), 'Dynamic Tool Mode row is missing');
+ Assert.AreEqual('Dynamic Tool Mode', MCPConfigCard.ServerFeatureList.Feature.Value, 'Unexpected second feature');
+ Assert.IsTrue(MCPConfigCard.ServerFeatureList.Next(), 'Data Query Tools row is missing');
+ Assert.AreEqual('Data Query Tools (Preview)', MCPConfigCard.ServerFeatureList.Feature.Value, 'Unexpected third feature');
+ Assert.IsFalse(MCPConfigCard.ServerFeatureList.Next(), 'Unexpected extra feature rows');
+ end;
+
+ [Test]
+ procedure TestConfigureEnabledOnlyForDynamicToolMode()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ MCPConfigCard: TestPage "MCP Config Card";
+ ConfigId: Guid;
+ begin
+ // [GIVEN] A non-default configuration with Dynamic Tool Mode active (so its row is configurable + active)
+ ConfigId := CreateMCPConfig(false, true, true, false);
+ MCPConfiguration.GetBySystemId(ConfigId);
+
+ // [WHEN] The configuration card is opened
+ MCPConfigCard.OpenEdit();
+ MCPConfigCard.GoToRecord(MCPConfiguration);
+
+ // [THEN] Configure is enabled only on Dynamic Tool Mode (the one feature with settings).
+ // NOTE: API Tools / Data Query are also inactive here (mock), which on its own disables Configure;
+ // Dynamic Tool Mode isolates the Configurable (HasSettings) gate since it is active.
+ MCPConfigCard.ServerFeatureList.First(); // API Tools
+ Assert.IsFalse(MCPConfigCard.ServerFeatureList.Configure.Enabled(), 'Configure should be disabled for API Tools');
+ MCPConfigCard.ServerFeatureList.Next(); // Dynamic Tool Mode
+ Assert.IsTrue(MCPConfigCard.ServerFeatureList.Configure.Enabled(), 'Configure should be enabled for Dynamic Tool Mode');
+ MCPConfigCard.ServerFeatureList.Next(); // Data Query Tools
+ Assert.IsFalse(MCPConfigCard.ServerFeatureList.Configure.Enabled(), 'Configure should be disabled for Data Query Tools');
+ end;
+
+ [Test]
+ procedure TestActivateDynamicToolModeFromServerFeatures()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ MCPConfigCard: TestPage "MCP Config Card";
+ ConfigId: Guid;
+ begin
+ // [GIVEN] A non-default configuration with API Tools enabled (Dynamic Tool Mode requires it) and
+ // Dynamic Tool Mode off
+ ConfigId := CreateMCPConfig(false, false, true, false);
+ MCPConfig.EnableAPITools(ConfigId, true);
+ MCPConfiguration.GetBySystemId(ConfigId);
+ MCPConfigCard.OpenEdit();
+ MCPConfigCard.GoToRecord(MCPConfiguration);
+
+ // [WHEN] Activate is invoked on the Dynamic Tool Mode row
+ MCPConfigCard.ServerFeatureList.First(); // API Tools
+ MCPConfigCard.ServerFeatureList.Next(); // Dynamic Tool Mode
+ Assert.AreEqual('Dynamic Tool Mode', MCPConfigCard.ServerFeatureList.Feature.Value, 'Not positioned on the Dynamic Tool Mode row');
+ MCPConfigCard.ServerFeatureList.Activate.Invoke();
+
+ // [THEN] The row turns Active and the configuration field is set
+ Assert.AreEqual('Active', MCPConfigCard.ServerFeatureList.Status.Value, 'Dynamic Tool Mode row is not Active');
+ MCPConfiguration.GetBySystemId(ConfigId);
+ Assert.IsTrue(MCPConfiguration.EnableDynamicToolMode, 'EnableDynamicToolMode was not set');
+ end;
+
+ [Test]
+ procedure TestServerFeaturesListHiddenOnDefaultConfiguration()
+ var
+ MCPConfiguration: Record "MCP Configuration";
+ MCPConfigCard: TestPage "MCP Config Card";
+ begin
+ // [GIVEN] The default configuration
+ MCPConfiguration.Get('');
+
+ // [WHEN] The configuration card is opened
+ MCPConfigCard.OpenEdit();
+ MCPConfigCard.GoToRecord(MCPConfiguration);
+
+ // [THEN] The Server Features list is not shown
+ Assert.IsFalse(MCPConfigCard.ServerFeatureList.Visible(), 'Server Features list should be hidden on the default configuration');
+ end;
+
+ #endregion
+
#region Connection String
[Test]
@@ -1386,17 +1549,10 @@ codeunit 130130 "MCP Config Test"
[ModalPageHandler]
- procedure LookupAPIPageToolsOKHandler(var MCPAPIConfigToolLookup: TestPage "MCP API Config Tool Lookup")
- begin
- MCPAPIConfigToolLookup.GoToKey(Page::"Mock API");
- MCPAPIConfigToolLookup.OK().Invoke();
- end;
-
- [ModalPageHandler]
- procedure LookupAPIQueryToolsOKHandler(var MCPQueryConfigToolLookup: TestPage "MCP Query Config Tool Lookup")
+ procedure LookupAPIObjectsOKHandler(var MCPAPIObjectLookup: TestPage "MCP API Object Lookup")
begin
- MCPQueryConfigToolLookup.GoToKey(Query::"Mock API Query");
- MCPQueryConfigToolLookup.OK().Invoke();
+ MCPAPIObjectLookup.First();
+ MCPAPIObjectLookup.OK().Invoke();
end;
[ModalPageHandler]