From ba86175d5807c5e979dd1a88af80c39d8634e236 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:05:38 -0400 Subject: [PATCH 1/2] Cleanup Fabric OneLake commands --- servers/Azure.Mcp.Server/TROUBLESHOOTING.md | 4 +- servers/Fabric.Mcp.Server/README.md | 28 +++---- .../src/Commands/BaseItemCommand.cs | 48 ++++++++++++ .../src/Commands/BaseWorkspaceCommand.cs | 48 ++++++++++++ .../src/Commands/File/BlobDeleteCommand.cs | 47 +----------- .../src/Commands/File/BlobGetCommand.cs | 63 ++------------- .../src/Commands/File/BlobListCommand.cs | 37 +-------- .../src/Commands/File/BlobPutCommand.cs | 61 +++++---------- .../Commands/File/DirectoryCreateCommand.cs | 76 +++++-------------- .../Commands/File/DirectoryDeleteCommand.cs | 48 ++---------- .../src/Commands/File/FileDeleteCommand.cs | 48 ++---------- .../src/Commands/File/FileReadCommand.cs | 58 ++------------ .../src/Commands/File/FileWriteCommand.cs | 47 ++---------- .../src/Commands/File/PathListCommand.cs | 44 +---------- .../Item/OneLakeItemDataListCommand.cs | 37 ++------- .../Commands/Item/OneLakeItemListCommand.cs | 41 ++-------- .../Item/OneLakeItemListDfsCommand.cs | 35 ++------- .../Commands/Table/TableConfigGetCommand.cs | 48 ++---------- .../src/Commands/Table/TableGetCommand.cs | 42 +--------- .../src/Commands/Table/TableListCommand.cs | 42 +--------- .../Table/TableNamespaceGetCommand.cs | 42 +--------- .../Table/TableNamespaceListCommand.cs | 48 ++---------- .../Workspace/OneLakeWorkspaceListCommand.cs | 5 +- .../src/FabricOneLakeSetup.cs | 1 - .../src/Models/BlobDownloadOptions.cs | 2 - .../src/Models/OneLakeJsonContext.cs | 1 - .../src/Models/TableConfigurationResult.cs | 1 - .../src/Models/TableGetResult.cs | 1 - .../src/Models/TableListResult.cs | 1 - .../src/Models/TableNamespaceGetResult.cs | 1 - .../src/Models/TableNamespaceListResult.cs | 1 - .../src/Options/BaseItemOptions.cs | 9 +++ ...gGetOptions.cs => BaseWorkspaceOptions.cs} | 5 +- .../src/Options/BlobListOptions.cs | 6 +- .../src/Options/FabricOptionDefinitions.cs | 2 - .../src/Options/OneLakeOptionDefinitions.cs | 2 - .../src/Options/PathListOptions.cs | 6 +- .../src/Options/TableGetOptions.cs | 8 +- .../src/Options/TableListOptions.cs | 8 +- .../src/Options/TableNamespaceGetOptions.cs | 8 +- .../src/Options/TableNamespaceListOptions.cs | 14 ---- .../src/Prompts/OneLakePrompts.cs | 2 +- .../src/Services/OneLakeService.cs | 71 ++++++++--------- .../tests/Commands/BlobGetCommandTests.cs | 3 +- .../tests/Commands/BlobPutCommandTests.cs | 4 +- .../Commands/DirectoryCreateCommandTests.cs | 4 +- .../Commands/DirectoryDeleteCommandTests.cs | 4 +- .../OneLakeItemDataListCommandTests.cs | 4 +- .../Commands/OneLakeItemListCommandTests.cs | 4 +- .../OneLakeWorkspaceListCommandTests.cs | 4 +- .../tests/Commands/PathListCommandTests.cs | 4 +- .../Table/TableConfigGetCommandTests.cs | 4 +- .../Commands/Table/TableGetCommandTests.cs | 4 +- .../Commands/Table/TableListCommandTests.cs | 4 +- .../Table/TableNamespaceGetCommandTests.cs | 4 +- .../Table/TableNamespaceListCommandTests.cs | 4 +- .../tests/FabricOneLakeSetupTests.cs | 28 +++---- 57 files changed, 312 insertions(+), 914 deletions(-) create mode 100644 tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs create mode 100644 tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs create mode 100644 tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseItemOptions.cs rename tools/Fabric.Mcp.Tools.OneLake/src/Options/{TableConfigGetOptions.cs => BaseWorkspaceOptions.cs} (52%) delete mode 100644 tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceListOptions.cs diff --git a/servers/Azure.Mcp.Server/TROUBLESHOOTING.md b/servers/Azure.Mcp.Server/TROUBLESHOOTING.md index 5c191a4397..8bf498bbf6 100644 --- a/servers/Azure.Mcp.Server/TROUBLESHOOTING.md +++ b/servers/Azure.Mcp.Server/TROUBLESHOOTING.md @@ -376,12 +376,12 @@ This error indicates that the access token doesn't have sufficient permissions t ### Service Principal Returns 403 for OneLake Operations in VS Code -When using Azure MCP Server inside VS Code with a Service Principal authenticated via Azure CLI (`az login --service-principal`), OneLake DFS operations (such as `directory_create`, `upload_file`) may return `403 Forbidden`, even though the Service Principal has the correct permissions (e.g., Workspace Admin in Microsoft Fabric). +When using Azure MCP Server inside VS Code with a Service Principal authenticated via Azure CLI (`az login --service-principal`), OneLake DFS operations (such as `directory_create`, `upload-file`) may return `403 Forbidden`, even though the Service Principal has the correct permissions (e.g., Workspace Admin in Microsoft Fabric). #### Symptoms - OneLake **blob** operations (e.g., `file_list` without a path) may succeed with `200 OK` -- OneLake **DFS** operations (e.g., `directory_create`, `upload_file`) fail with `403 Forbidden` +- OneLake **DFS** operations (e.g., `directory_create`, `upload-file`) fail with `403 Forbidden` - The same Service Principal works correctly when used from Python scripts or other tools outside VS Code - Fabric REST API operations (e.g., `item_create`) may also fail with `401 Unauthorized` diff --git a/servers/Fabric.Mcp.Server/README.md b/servers/Fabric.Mcp.Server/README.md index 9935b73fae..2d934b8031 100644 --- a/servers/Fabric.Mcp.Server/README.md +++ b/servers/Fabric.Mcp.Server/README.md @@ -239,20 +239,20 @@ The Fabric MCP Server exposes tools organized into three categories: | Tool Name | Description | |-----------|-------------| -| `onelake_list_workspaces` | Lists available Microsoft Fabric workspaces. | -| `onelake_list_items` | Lists workspace items with high-level metadata. | -| `onelake_list_items_dfs` | Lists Fabric items via the DFS endpoint. | -| `onelake_list_files` | Lists files using the hierarchical file-list endpoint. | -| `onelake_download_file` | Downloads a OneLake file. | -| `onelake_upload_file` | Uploads a file to OneLake storage. | -| `onelake_delete_file` | Deletes a file from OneLake storage. | -| `onelake_create_directory` | Creates a directory via the DFS endpoint. | -| `onelake_delete_directory` | Deletes a directory (optionally recursive). | -| `onelake_get_table_config` | Retrieves table API configuration for a workspace item. | -| `onelake_list_table_namespaces` | Lists table namespaces (schemas) exposed through the table API. | -| `onelake_get_table_namespace` | Retrieves metadata for a specific namespace. | -| `onelake_list_tables` | Lists tables published within a namespace. | -| `onelake_get_table` | Retrieves the definition for a specific table. | +| `onelake_list-workspaces` | Lists available Microsoft Fabric workspaces. | +| `onelake_list-items` | Lists workspace items with high-level metadata. | +| `onelake_list-items-dfs` | Lists Fabric items via the DFS endpoint. | +| `onelake_list-files` | Lists files using the hierarchical file-list endpoint. | +| `onelake_download-file` | Downloads a OneLake file. | +| `onelake_upload-file` | Uploads a file to OneLake storage. | +| `onelake_delete-file` | Deletes a file from OneLake storage. | +| `onelake_create-directory` | Creates a directory via the DFS endpoint. | +| `onelake_delete-directory` | Deletes a directory (optionally recursive). | +| `onelake_get-table-config` | Retrieves table API configuration for a workspace item. | +| `onelake_list-table-namespaces` | Lists table namespaces (schemas) exposed through the table API. | +| `onelake_get-table-namespace` | Retrieves metadata for a specific namespace. | +| `onelake_list-tables` | Lists tables published within a namespace. | +| `onelake_get-table` | Retrieves the definition for a specific table. | ### Core Fabric Operations diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs new file mode 100644 index 0000000000..3a932bd6da --- /dev/null +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using Azure.Mcp.Core.Commands; +using Azure.Mcp.Core.Extensions; +using Fabric.Mcp.Tools.OneLake.Options; +using Microsoft.Mcp.Core.Extensions; +using Microsoft.Mcp.Core.Models.Option; + +namespace Fabric.Mcp.Tools.OneLake.Commands; + +public abstract class BaseItemCommand< + [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions> + : BaseWorkspaceCommand where TOptions : BaseItemOptions, new() +{ + protected BaseItemCommand() + { + } + + protected override void RegisterOptions(Command command) + { + base.RegisterOptions(command); + command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); + command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); + command.Validators.Add(result => + { + var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); + var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); + + if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) + { + result.AddError("Item identifier is required. Provide --item or --item-id."); + } + }); + } + + protected override TOptions BindOptions(ParseResult parseResult) + { + var options = base.BindOptions(parseResult); + var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); + var item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); + options.ItemId = !string.IsNullOrWhiteSpace(itemId) + ? itemId! + : item ?? string.Empty; + return options; + } +} diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs new file mode 100644 index 0000000000..6f198591c6 --- /dev/null +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using Azure.Mcp.Core.Commands; +using Azure.Mcp.Core.Extensions; +using Fabric.Mcp.Tools.OneLake.Options; +using Microsoft.Mcp.Core.Extensions; +using Microsoft.Mcp.Core.Models.Option; + +namespace Fabric.Mcp.Tools.OneLake.Commands; + +public abstract class BaseWorkspaceCommand< + [DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions> + : GlobalCommand where TOptions : BaseWorkspaceOptions, new() +{ + protected BaseWorkspaceCommand() + { + } + + protected override void RegisterOptions(Command command) + { + base.RegisterOptions(command); + command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); + command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); + command.Validators.Add(result => + { + var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); + var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); + + if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) + { + result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); + } + }); + } + + protected override TOptions BindOptions(ParseResult parseResult) + { + var options = base.BindOptions(parseResult); + var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); + var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); + options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) + ? workspaceId! + : workspaceName ?? string.Empty; + return options; + } +} diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobDeleteCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobDeleteCommand.cs index 73f837e0e1..deb75f76ca 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobDeleteCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobDeleteCommand.cs @@ -2,24 +2,21 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; [HiddenCommand] public sealed class BlobDeleteCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -42,46 +39,12 @@ public sealed class BlobDeleteCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override BlobDeleteOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; return options; } @@ -97,8 +60,8 @@ public override async Task ExecuteAsync(CommandContext context, try { var result = await _oneLakeService.DeleteBlobAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, cancellationToken); @@ -128,9 +91,7 @@ public sealed record BlobDeleteCommandResult(BlobDeleteResult Result, string Mes }; } -public sealed class BlobDeleteOptions : GlobalOptions +public sealed class BlobDeleteOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobGetCommand.cs index bdd018fd62..8b9b0d0a31 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobGetCommand.cs @@ -3,22 +3,20 @@ using System.Net; using System.Text; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.Mcp.Core.Areas.Server.Options; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; using Microsoft.Mcp.Core.Models.Option; +namespace Fabric.Mcp.Tools.OneLake.Commands.File; + public sealed class BlobGetCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -26,7 +24,7 @@ public sealed class BlobGetCommand( private const long InlineContentLimitBytes = 1 * 1024 * 1024; // 1 MiB inline payload limit public override string Id => "75d6cb4c-4e81-4e69-a4ec-eca53a7dacd9"; - public override string Name => "download_file"; + public override string Name => "download-file"; public override string Title => "Download OneLake File"; public override string Description => "Downloads a file from OneLake storage. Use this when the user needs to retrieve file content or metadata. Returns base64 content, metadata, and text when applicable."; @@ -34,7 +32,7 @@ public sealed class BlobGetCommand( { Destructive = false, Idempotent = true, - LocalRequired = false, + LocalRequired = true, OpenWorld = false, ReadOnly = true, Secret = false @@ -43,47 +41,13 @@ public sealed class BlobGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); command.Options.Add(FabricOptionDefinitions.DownloadFilePath.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override BlobGetOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; options.DownloadFilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.DownloadFilePath.Name); return options; @@ -99,18 +63,9 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var serviceStartOptions = context.GetService>(); - var transport = serviceStartOptions.Value.Transport ?? "stdio"; - var isLocalTransport = string.Equals(transport, "stdio", StringComparison.OrdinalIgnoreCase); - string? downloadPath = null; if (!string.IsNullOrWhiteSpace(options.DownloadFilePath)) { - if (!isLocalTransport) - { - throw new ArgumentException("The --download-file-path option is only supported when the server runs with stdio transport.", nameof(options.DownloadFilePath)); - } - var candidatePath = options.DownloadFilePath!; downloadPath = Path.IsPathRooted(candidatePath) ? candidatePath @@ -136,8 +91,8 @@ public override async Task ExecuteAsync(CommandContext context, }; var result = await _oneLakeService.GetBlobAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, downloadOptions, cancellationToken); @@ -186,10 +141,8 @@ public sealed record BlobGetCommandResult(BlobGetResult Blob, string Message); }; } -public sealed class BlobGetOptions : GlobalOptions +public sealed class BlobGetOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public string? DownloadFilePath { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobListCommand.cs index 6029207754..aa44bd86d6 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobListCommand.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models; using Fabric.Mcp.Tools.OneLake.Models; @@ -10,14 +9,13 @@ using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; [HiddenCommand] public sealed class BlobListCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -40,47 +38,14 @@ public sealed class BlobListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.Path); command.Options.Add(FabricOptionDefinitions.Recursive); command.Options.Add(OneLakeOptionDefinitions.Format); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override BlobListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.Path = parseResult.GetValueOrDefault(FabricOptionDefinitions.Path.Name); options.Recursive = parseResult.GetValueOrDefault(FabricOptionDefinitions.Recursive.Name); options.Format = parseResult.GetValueOrDefault(OneLakeOptionDefinitions.Format.Name); diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobPutCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobPutCommand.cs index c5c3c5a6f0..79f6519de5 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobPutCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/BlobPutCommand.cs @@ -3,28 +3,25 @@ using System.Net; using System.Text; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; public sealed class BlobPutCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "f6b3249d-6481-4e80-9d34-0d6867718dd7"; - public override string Name => "upload_file"; + public override string Name => "upload-file"; public override string Title => "Upload OneLake File"; public override string Description => "Uploads a file to OneLake storage from inline content or local file path. Use this when the user needs to store data in OneLake. Supports overwrite control and content type specification."; @@ -32,7 +29,7 @@ public sealed class BlobPutCommand( { Destructive = true, Idempotent = false, - LocalRequired = false, + LocalRequired = true, OpenWorld = false, ReadOnly = false, Secret = false @@ -41,10 +38,6 @@ public sealed class BlobPutCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); command.Options.Add(FabricOptionDefinitions.Content); command.Options.Add(FabricOptionDefinitions.LocalFilePath); @@ -52,19 +45,23 @@ protected override void RegisterOptions(Command command) command.Options.Add(FabricOptionDefinitions.ContentType); command.Validators.Add(result => { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) + var localFilePath = result.GetValueOrDefault(FabricOptionDefinitions.LocalFilePath.Name); + var content = result.GetValueOrDefault(FabricOptionDefinitions.Content.Name); + if (string.IsNullOrEmpty(localFilePath) && string.IsNullOrEmpty(content)) + { + result.AddError("Either --content or --local-file-path must be specified."); + } + else if (!string.IsNullOrEmpty(localFilePath) && !string.IsNullOrEmpty(content)) { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); + result.AddError("Only one of --content or --local-file-path can be specified, not both."); } - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) + if (!string.IsNullOrEmpty(localFilePath)) { - result.AddError("Item identifier is required. Provide --item or --item-id."); + if (!System.IO.File.Exists(localFilePath)) + { + result.AddError($"Local file not found: {localFilePath}"); + } } }); } @@ -72,19 +69,6 @@ protected override void RegisterOptions(Command command) protected override BlobPutOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; options.Content = parseResult.GetValueOrDefault(FabricOptionDefinitions.Content.Name); options.LocalFilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.LocalFilePath.Name); @@ -107,8 +91,8 @@ public override async Task ExecuteAsync(CommandContext context, using var contentStream = ResolveContentStream(options, out var contentLength); var result = await _oneLakeService.PutBlobAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, contentStream, contentLength, @@ -153,11 +137,6 @@ private static Stream ResolveContentStream(BlobPutOptions options, out long cont { if (!string.IsNullOrEmpty(options.LocalFilePath)) { - if (!System.IO.File.Exists(options.LocalFilePath)) - { - throw new FileNotFoundException($"Local file not found: {options.LocalFilePath}"); - } - var fileStream = System.IO.File.OpenRead(options.LocalFilePath); contentLength = fileStream.Length; return fileStream; @@ -194,10 +173,8 @@ public sealed record BlobPutCommandResult( string Message); } -public sealed class BlobPutOptions : GlobalOptions +public sealed class BlobPutOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public string? Content { get; set; } public string? LocalFilePath { get; set; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryCreateCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryCreateCommand.cs index 9f00973afb..af4b4b24ec 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryCreateCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryCreateCommand.cs @@ -2,16 +2,13 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; @@ -20,13 +17,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.File; /// public sealed class DirectoryCreateCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "0c4cf0f4-2ef4-4f1d-9f80-24fd7636d5fe"; - public override string Name => "create_directory"; + public override string Name => "create-directory"; public override string Title => "Create OneLake Directory"; public override string Description => "Creates a directory in OneLake storage. Use this when the user needs to organize files or prepare folder structures. Can create nested directory paths."; @@ -43,45 +40,12 @@ public sealed class DirectoryCreateCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.DirectoryPath); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override DirectoryCreateOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.DirectoryPath = parseResult.GetValueOrDefault(FabricOptionDefinitions.DirectoryPath.Name) ?? string.Empty; return options; } @@ -98,19 +62,17 @@ public override async Task ExecuteAsync(CommandContext context, try { await _oneLakeService.CreateDirectoryAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.DirectoryPath, cancellationToken); - var result = new DirectoryCreateCommandResult - { - WorkspaceId = options.WorkspaceId, - ItemId = options.ItemId, - DirectoryPath = options.DirectoryPath, - Success = true, - Message = $"Directory '{options.DirectoryPath}' created successfully" - }; + var result = new DirectoryCreateCommandResult( + WorkspaceId: options.WorkspaceId!, + ItemId: options.ItemId!, + DirectoryPath: options.DirectoryPath, + Success: true, + Message: $"Directory '{options.DirectoryPath}' created successfully"); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.DirectoryCreateCommandResult); } @@ -142,19 +104,15 @@ HttpRequestException httpEx when httpEx.Message.Contains("401") => HttpStatusCod _ => base.GetStatusCode(ex) }; - public sealed record DirectoryCreateCommandResult - { - public string WorkspaceId { get; init; } = string.Empty; - public string ItemId { get; init; } = string.Empty; - public string DirectoryPath { get; init; } = string.Empty; - public bool Success { get; init; } - public string Message { get; init; } = string.Empty; - } + public sealed record DirectoryCreateCommandResult( + string WorkspaceId, + string ItemId, + string DirectoryPath, + bool Success, + string Message); } -public sealed class DirectoryCreateOptions : GlobalOptions +public sealed class DirectoryCreateOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string DirectoryPath { get; set; } = string.Empty; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryDeleteCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryDeleteCommand.cs index 58b5c50204..0ea5dbed32 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryDeleteCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/DirectoryDeleteCommand.cs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; public sealed class DirectoryDeleteCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "86991cd6-75fa-4870-9d99-f986ba9f5f73"; - public override string Name => "delete_directory"; + public override string Name => "delete-directory"; public override string Title => "Delete OneLake Directory"; public override string Description => "Deletes a directory from OneLake storage. Use this when the user wants to remove a folder. Use recursive flag to delete non-empty directories."; @@ -39,46 +36,13 @@ public sealed class DirectoryDeleteCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.DirectoryPath); command.Options.Add(FabricOptionDefinitions.Recursive); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override DirectoryDeleteOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.DirectoryPath = parseResult.GetValueOrDefault(FabricOptionDefinitions.DirectoryPath.Name) ?? string.Empty; options.Recursive = parseResult.GetValueOrDefault(FabricOptionDefinitions.Recursive.Name); return options; @@ -95,8 +59,8 @@ public override async Task ExecuteAsync(CommandContext context, try { await _oneLakeService.DeleteDirectoryAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.DirectoryPath, options.Recursive, cancellationToken); @@ -122,10 +86,8 @@ public sealed record DirectoryDeleteCommandResult( string Message); } -public sealed class DirectoryDeleteOptions : GlobalOptions +public sealed class DirectoryDeleteOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string DirectoryPath { get; set; } = string.Empty; public bool Recursive { get; set; } = false; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileDeleteCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileDeleteCommand.cs index a2349e5718..811a3ec093 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileDeleteCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileDeleteCommand.cs @@ -2,28 +2,25 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; public sealed class FileDeleteCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "0aa3f887-0085-4141-8e34-f0cf1ed44f71"; - public override string Name => "delete_file"; + public override string Name => "delete-file"; public override string Title => "Delete OneLake File"; public override string Description => "Deletes a file from OneLake storage. Use this when the user wants to remove a specific file. Permanently removes the file at the specified path."; @@ -40,45 +37,12 @@ public sealed class FileDeleteCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override FileDeleteOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; return options; } @@ -94,8 +58,8 @@ public override async Task ExecuteAsync(CommandContext context, try { await _oneLakeService.DeleteFileAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, cancellationToken); @@ -123,9 +87,7 @@ public sealed record FileDeleteCommandResult( }; } -public sealed class FileDeleteOptions : GlobalOptions +public sealed class FileDeleteOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileReadCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileReadCommand.cs index e35557d923..1231961de9 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileReadCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileReadCommand.cs @@ -3,16 +3,12 @@ using System.Net; using System.Text; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.Mcp.Core.Areas.Server.Options; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; using Microsoft.Mcp.Core.Models.Option; @@ -22,7 +18,7 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.File; [HiddenCommand] public sealed class FileReadCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -38,7 +34,7 @@ public sealed class FileReadCommand( { Destructive = false, Idempotent = true, - LocalRequired = false, + LocalRequired = true, OpenWorld = false, ReadOnly = true, Secret = false @@ -47,46 +43,13 @@ public sealed class FileReadCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); command.Options.Add(FabricOptionDefinitions.DownloadFilePath.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override FileReadOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; options.DownloadFilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.DownloadFilePath.Name); return options; @@ -102,18 +65,9 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var serviceStartOptions = context.GetService>(); - var transport = serviceStartOptions.Value.Transport ?? "stdio"; - var isLocalTransport = string.Equals(transport, "stdio", StringComparison.OrdinalIgnoreCase); - string? downloadPath = null; if (!string.IsNullOrWhiteSpace(options.DownloadFilePath)) { - if (!isLocalTransport) - { - throw new ArgumentException("The --download-file-path option is only supported when the server runs with stdio transport.", nameof(options.DownloadFilePath)); - } - var candidatePath = options.DownloadFilePath!; downloadPath = Path.IsPathRooted(candidatePath) ? candidatePath @@ -139,8 +93,8 @@ public override async Task ExecuteAsync(CommandContext context, }; var blobResult = await _oneLakeService.ReadFileAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, downloadOptions, cancellationToken); @@ -217,10 +171,8 @@ public sealed record FileReadCommandResult( }; } -public sealed class FileReadOptions : GlobalOptions +public sealed class FileReadOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public string? DownloadFilePath { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileWriteCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileWriteCommand.cs index 1cc3a0d5d9..0f77cefa89 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileWriteCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/FileWriteCommand.cs @@ -1,24 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Azure.Mcp.Core.Models; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; [HiddenCommand] public sealed class FileWriteCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -32,7 +29,7 @@ public sealed class FileWriteCommand( { Destructive = true, Idempotent = false, - LocalRequired = false, + LocalRequired = true, OpenWorld = false, ReadOnly = false, Secret = false @@ -41,31 +38,12 @@ public sealed class FileWriteCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.FilePath); command.Options.Add(FabricOptionDefinitions.Content); command.Options.Add(FabricOptionDefinitions.LocalFilePath); command.Options.Add(FabricOptionDefinitions.Overwrite); command.Validators.Add(result => { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - var content = result.GetValueOrDefault(FabricOptionDefinitions.Content.Name); var localFilePath = result.GetValueOrDefault(FabricOptionDefinitions.LocalFilePath.Name); if (string.IsNullOrWhiteSpace(content) && string.IsNullOrWhiteSpace(localFilePath)) @@ -88,19 +66,6 @@ protected override void RegisterOptions(Command command) protected override FileWriteOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.FilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.FilePath.Name) ?? string.Empty; options.Content = parseResult.GetValueOrDefault(FabricOptionDefinitions.Content.Name); options.LocalFilePath = parseResult.GetValueOrDefault(FabricOptionDefinitions.LocalFilePath.Name); @@ -138,8 +103,8 @@ public override async Task ExecuteAsync(CommandContext context, using (contentStream) { await _oneLakeService.WriteFileAsync( - options.WorkspaceId, - options.ItemId, + options.WorkspaceId!, + options.ItemId!, options.FilePath, contentStream, options.Overwrite, @@ -169,10 +134,8 @@ public sealed record FileWriteCommandResult( string Message); } -public sealed class FileWriteOptions : GlobalOptions +public sealed class FileWriteOptions : BaseItemOptions { - public string WorkspaceId { get; set; } = string.Empty; - public string ItemId { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public string? Content { get; set; } public string? LocalFilePath { get; set; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/PathListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/PathListCommand.cs index 7f63388e22..7e3db9e4a3 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/PathListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/File/PathListCommand.cs @@ -1,12 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; -using System.CommandLine; -using System.CommandLine.Parsing; -using System.Text.Json; -using System.Threading; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; @@ -14,20 +8,19 @@ using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Command; using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.File; public sealed class PathListCommand(ILogger logger) - : GlobalCommand() + : BaseItemCommand() { private const string CommandTitle = "List OneLake Path Structure"; private readonly ILogger _logger = logger; public override string Id => "3bf1b82d-ff44-4984-9b97-0e6d9e4917a3"; - public override string Name => "list_files"; + public override string Name => "list-files"; public override string Description => """ @@ -56,47 +49,14 @@ providing comprehensive visibility across all top-level OneLake folders. protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); command.Options.Add(FabricOptionDefinitions.Path.AsOptional()); command.Options.Add(FabricOptionDefinitions.Recursive.AsOptional()); command.Options.Add(OneLakeOptionDefinitions.Format.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override PathListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; - - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var itemName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : itemName ?? string.Empty; - options.Path = parseResult.GetValueOrDefault(FabricOptionDefinitions.Path.Name); options.Recursive = parseResult.GetValueOrDefault(FabricOptionDefinitions.Recursive.Name); options.Format = parseResult.GetValueOrDefault(OneLakeOptionDefinitions.Format.Name); diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemDataListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemDataListCommand.cs index 07ba96537a..76656968e1 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemDataListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemDataListCommand.cs @@ -2,16 +2,13 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.Item; @@ -20,13 +17,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Item; /// public sealed class OneLakeItemDataListCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseWorkspaceCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "8925d0c4-becf-4b5a-8af1-3e998c1058ec"; - public override string Name => "list_items_dfs"; + public override string Name => "list-items-dfs"; public override string Title => "List OneLake Items (Data API)"; public override string Description => "List OneLake items in a workspace using the OneLake DFS (Data Lake File System) data API."; @@ -43,30 +40,13 @@ public sealed class OneLakeItemDataListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); command.Options.Add(FabricOptionDefinitions.Recursive); command.Options.Add(FabricOptionDefinitions.ContinuationToken); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - }); } protected override OneLakeItemDataListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; options.Recursive = parseResult.GetValueOrDefault(FabricOptionDefinitions.Recursive.Name); options.ContinuationToken = parseResult.GetValueOrDefault(FabricOptionDefinitions.ContinuationToken.Name); return options; @@ -83,13 +63,12 @@ public override async Task ExecuteAsync(CommandContext context, try { var jsonResponse = await _oneLakeService.ListOneLakeItemsDfsJsonAsync( - options.WorkspaceId, + options.WorkspaceId!, recursive: options.Recursive, continuationToken: options.ContinuationToken, cancellationToken); - var result = new OneLakeItemDataListCommandResult { JsonResponse = jsonResponse }; - context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.OneLakeItemDataListCommandResult); + context.Response.Results = ResponseResult.Create(new(jsonResponse), OneLakeJsonContext.Default.OneLakeItemDataListCommandResult); } catch (Exception ex) { @@ -118,15 +97,11 @@ HttpRequestException httpEx when httpEx.Message.Contains("401") => HttpStatusCod _ => base.GetStatusCode(ex) }; - public sealed record OneLakeItemDataListCommandResult - { - public string? JsonResponse { get; init; } - } + public sealed record OneLakeItemDataListCommandResult(string? JsonResponse); } -public sealed class OneLakeItemDataListOptions : GlobalOptions +public sealed class OneLakeItemDataListOptions : BaseWorkspaceOptions { - public string WorkspaceId { get; set; } = string.Empty; public bool Recursive { get; set; } public string? ContinuationToken { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListCommand.cs index 3a69c5690c..f9a0c3e316 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListCommand.cs @@ -2,16 +2,13 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.Item; @@ -20,13 +17,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Item; /// public sealed class OneLakeItemListCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseWorkspaceCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "61eb86d8-3879-4d2d-969a-6c96f2e0ce0d"; - public override string Name => "list_items"; + public override string Name => "list-items"; public override string Title => "List OneLake Items"; public override string Description => "Lists OneLake items in a Fabric workspace using the high-level OneLake API. Use this when the user needs to see what items exist in a workspace. Returns item names, types, and metadata."; @@ -43,29 +40,12 @@ public sealed class OneLakeItemListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); command.Options.Add(FabricOptionDefinitions.ContinuationToken); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - }); } protected override OneLakeItemListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; options.ContinuationToken = parseResult.GetValueOrDefault(FabricOptionDefinitions.ContinuationToken.Name); return options; } @@ -81,12 +61,11 @@ public override async Task ExecuteAsync(CommandContext context, try { var xmlResponse = await _oneLakeService.ListOneLakeItemsXmlAsync( - options.WorkspaceId, + options.WorkspaceId!, continuationToken: options.ContinuationToken, cancellationToken); - var result = new OneLakeItemListCommandResult { XmlResponse = xmlResponse }; - context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.OneLakeItemListCommandResult); + context.Response.Results = ResponseResult.Create(new(xmlResponse), OneLakeJsonContext.Default.OneLakeItemListCommandResult); } catch (Exception ex) { @@ -115,18 +94,10 @@ HttpRequestException httpEx when httpEx.Message.Contains("401") => HttpStatusCod _ => base.GetStatusCode(ex) }; - public sealed record OneLakeItemListCommandResult - { - public List? Items { get; init; } - public string? XmlResponse { get; init; } - - public OneLakeItemListCommandResult() { } - public OneLakeItemListCommandResult(List items) { Items = items; } - } + public sealed record OneLakeItemListCommandResult(string? XmlResponse); } -public sealed class OneLakeItemListOptions : GlobalOptions +public sealed class OneLakeItemListOptions : BaseWorkspaceOptions { - public string WorkspaceId { get; set; } = string.Empty; public string? ContinuationToken { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListDfsCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListDfsCommand.cs index 16d289d6ad..230a80bc71 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListDfsCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Item/OneLakeItemListDfsCommand.cs @@ -2,16 +2,13 @@ // Licensed under the MIT License. using System.Net; -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.Item; @@ -20,7 +17,7 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Item; /// public sealed class OneLakeItemListDfsCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseWorkspaceCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); @@ -43,30 +40,13 @@ public sealed class OneLakeItemListDfsCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); command.Options.Add(FabricOptionDefinitions.Recursive); command.Options.Add(FabricOptionDefinitions.ContinuationToken); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - }); } protected override OneLakeItemListDfsOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; options.Recursive = parseResult.GetValueOrDefault(FabricOptionDefinitions.Recursive.Name); options.ContinuationToken = parseResult.GetValueOrDefault(FabricOptionDefinitions.ContinuationToken.Name); return options; @@ -83,13 +63,12 @@ public override async Task ExecuteAsync(CommandContext context, try { var jsonResponse = await _oneLakeService.ListOneLakeItemsDfsJsonAsync( - options.WorkspaceId, + options.WorkspaceId!, recursive: options.Recursive, continuationToken: options.ContinuationToken, cancellationToken); - var result = new OneLakeItemListDfsCommandResult { JsonResponse = jsonResponse }; - context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.OneLakeItemListDfsCommandResult); + context.Response.Results = ResponseResult.Create(new(jsonResponse), OneLakeJsonContext.Default.OneLakeItemListDfsCommandResult); } catch (Exception ex) { @@ -118,15 +97,11 @@ HttpRequestException httpEx when httpEx.Message.Contains("401") => HttpStatusCod _ => base.GetStatusCode(ex) }; - public sealed record OneLakeItemListDfsCommandResult - { - public string? JsonResponse { get; init; } - } + public sealed record OneLakeItemListDfsCommandResult(string? JsonResponse); } -public sealed class OneLakeItemListDfsOptions : GlobalOptions +public sealed class OneLakeItemListDfsOptions : BaseWorkspaceOptions { - public string WorkspaceId { get; set; } = string.Empty; public bool Recursive { get; set; } public string? ContinuationToken { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableConfigGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableConfigGetCommand.cs index 193b8a2c71..bc82bf5abd 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableConfigGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableConfigGetCommand.cs @@ -1,27 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; -using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.Table; public sealed class TableConfigGetCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "bc15c475-0329-4cc3-aaa8-0e9f3fbde6f8"; - public override string Name => "get_table_config"; + public override string Name => "get-table-config"; public override string Title => "Get OneLake Table Configuration"; public override string Description => "Retrieves table API configuration for OneLake. Use this when the user needs to understand table access settings."; @@ -38,37 +34,11 @@ public sealed class TableConfigGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } - protected override TableConfigGetOptions BindOptions(ParseResult parseResult) + protected override BaseItemOptions BindOptions(ParseResult parseResult) { - var options = base.BindOptions(parseResult); - options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - options.Workspace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - options.Item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - return options; + return base.BindOptions(parseResult); } public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) @@ -81,15 +51,7 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var workspaceIdentifier = !string.IsNullOrWhiteSpace(options.WorkspaceId) - ? options.WorkspaceId - : options.Workspace; - - var itemIdentifier = !string.IsNullOrWhiteSpace(options.ItemId) - ? options.ItemId - : options.Item; - - var configuration = await _oneLakeService.GetTableConfigurationAsync(workspaceIdentifier!, itemIdentifier!, cancellationToken); + var configuration = await _oneLakeService.GetTableConfigurationAsync(options.WorkspaceId!, options.ItemId!, cancellationToken); var result = new TableConfigGetCommandResult(configuration.Workspace, configuration.Item, configuration.Configuration, configuration.RawResponse); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.TableConfigGetCommandResult); diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs index 0c942229f9..efa6dfd469 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; @@ -15,13 +14,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Table; public sealed class TableGetCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "19bb5a6a-2a09-410c-bfa0-312986c6acc6"; - public override string Name => "get_table"; + public override string Name => "get-table"; public override string Title => "Get OneLake Table"; public override string Description => "Retrieves table definition from OneLake. Use this when the user needs table schema or metadata."; @@ -38,39 +37,14 @@ public sealed class TableGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Namespace.AsRequired()); + command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); command.Options.Add(FabricOptionDefinitions.Table.AsRequired()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override TableGetOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - options.Workspace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - options.Item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); options.Table = parseResult.GetValueOrDefault(FabricOptionDefinitions.Table.Name); @@ -87,15 +61,7 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var workspaceIdentifier = !string.IsNullOrWhiteSpace(options.WorkspaceId) - ? options.WorkspaceId - : options.Workspace; - - var itemIdentifier = !string.IsNullOrWhiteSpace(options.ItemId) - ? options.ItemId - : options.Item; - - var tableResult = await _oneLakeService.GetTableAsync(workspaceIdentifier!, itemIdentifier!, options.Namespace!, options.Table!, cancellationToken); + var tableResult = await _oneLakeService.GetTableAsync(options.WorkspaceId!, options.ItemId!, options.Namespace!, options.Table!, cancellationToken); var result = new TableGetCommandResult(tableResult.Workspace, tableResult.Item, tableResult.Namespace, tableResult.Table, tableResult.Definition, tableResult.RawResponse); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.TableGetCommandResult); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs index 2f7df7efa3..b2e28e8c6b 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; @@ -15,13 +14,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Table; public sealed class TableListCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "7b1688e5-2a16-475d-8fd1-9bf3b0acf4f7"; - public override string Name => "list_tables"; + public override string Name => "list-tables"; public override string Title => "List OneLake Tables"; public override string Description => "Lists tables in OneLake. Use this when the user needs to see available tables."; @@ -38,38 +37,13 @@ public sealed class TableListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Namespace.AsRequired()); + command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override TableListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - options.Workspace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - options.Item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); return options; @@ -85,15 +59,7 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var workspaceIdentifier = !string.IsNullOrWhiteSpace(options.WorkspaceId) - ? options.WorkspaceId - : options.Workspace; - - var itemIdentifier = !string.IsNullOrWhiteSpace(options.ItemId) - ? options.ItemId - : options.Item; - - var tablesResult = await _oneLakeService.ListTablesAsync(workspaceIdentifier!, itemIdentifier!, options.Namespace!, cancellationToken); + var tablesResult = await _oneLakeService.ListTablesAsync(options.WorkspaceId!, options.ItemId!, options.Namespace!, cancellationToken); var result = new TableListCommandResult(tablesResult.Workspace, tablesResult.Item, tablesResult.Namespace, tablesResult.Tables, tablesResult.RawResponse); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.TableListCommandResult); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs index abf6812193..1cc4003a20 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; @@ -15,13 +14,13 @@ namespace Fabric.Mcp.Tools.OneLake.Commands.Table; public sealed class TableNamespaceGetCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "a86298d1-7475-4ea8-8c1b-e4c54ac2b896"; - public override string Name => "get_table_namespace"; + public override string Name => "get-table-namespace"; public override string Title => "Get OneLake Table Namespace"; public override string Description => "Retrieves metadata for a specific table namespace. Use this when the user needs details about a namespace."; @@ -38,38 +37,13 @@ public sealed class TableNamespaceGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Namespace.AsRequired()); + command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } protected override TableNamespaceGetOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - options.Workspace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - options.Item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); return options; @@ -85,15 +59,7 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var workspaceIdentifier = !string.IsNullOrWhiteSpace(options.WorkspaceId) - ? options.WorkspaceId - : options.Workspace; - - var itemIdentifier = !string.IsNullOrWhiteSpace(options.ItemId) - ? options.ItemId - : options.Item; - - var namespaceResult = await _oneLakeService.GetTableNamespaceAsync(workspaceIdentifier!, itemIdentifier!, options.Namespace!, cancellationToken); + var namespaceResult = await _oneLakeService.GetTableNamespaceAsync(options.WorkspaceId!, options.ItemId!, options.Namespace!, cancellationToken); var result = new TableNamespaceGetCommandResult(namespaceResult.Workspace, namespaceResult.Item, namespaceResult.Namespace, namespaceResult.Definition, namespaceResult.RawResponse); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.TableNamespaceGetCommandResult); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceListCommand.cs index 73b9108693..a0699aab15 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceListCommand.cs @@ -1,27 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Commands; -using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging; using Microsoft.Mcp.Core.Commands; -using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands.Table; public sealed class TableNamespaceListCommand( ILogger logger, - IOneLakeService oneLakeService) : GlobalCommand() + IOneLakeService oneLakeService) : BaseItemCommand() { private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "173cfc00-7c12-486d-a0e7-c0d4c1de23fd"; - public override string Name => "list_table_namespaces"; + public override string Name => "list-table-namespaces"; public override string Title => "List OneLake Table Namespaces"; public override string Description => "Lists table namespaces in OneLake. Use this when the user needs to discover available table namespaces."; @@ -38,37 +34,11 @@ public sealed class TableNamespaceListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); } - protected override TableNamespaceListOptions BindOptions(ParseResult parseResult) + protected override BaseItemOptions BindOptions(ParseResult parseResult) { - var options = base.BindOptions(parseResult); - options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - options.Workspace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - options.Item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - return options; + return base.BindOptions(parseResult); } public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken) @@ -81,15 +51,7 @@ public override async Task ExecuteAsync(CommandContext context, var options = BindOptions(parseResult); try { - var workspaceIdentifier = !string.IsNullOrWhiteSpace(options.WorkspaceId) - ? options.WorkspaceId - : options.Workspace; - - var itemIdentifier = !string.IsNullOrWhiteSpace(options.ItemId) - ? options.ItemId - : options.Item; - - var namespaceResult = await _oneLakeService.ListTableNamespacesAsync(workspaceIdentifier!, itemIdentifier!, cancellationToken); + var namespaceResult = await _oneLakeService.ListTableNamespacesAsync(options.WorkspaceId!, options.ItemId!, cancellationToken); var result = new TableNamespaceListCommandResult(namespaceResult.Workspace, namespaceResult.Item, namespaceResult.Namespaces, namespaceResult.RawResponse); context.Response.Results = ResponseResult.Create(result, OneLakeJsonContext.Default.TableNamespaceListCommandResult); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Workspace/OneLakeWorkspaceListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Workspace/OneLakeWorkspaceListCommand.cs index a245e35970..47ea27b09d 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Workspace/OneLakeWorkspaceListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Workspace/OneLakeWorkspaceListCommand.cs @@ -1,11 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.CommandLine; -using System.CommandLine.Parsing; using Azure.Mcp.Core.Commands; using Azure.Mcp.Core.Extensions; -using Azure.Mcp.Core.Options; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Options; using Fabric.Mcp.Tools.OneLake.Services; @@ -23,7 +20,7 @@ public sealed class OneLakeWorkspaceListCommand( private readonly IOneLakeService _oneLakeService = oneLakeService ?? throw new ArgumentNullException(nameof(oneLakeService)); public override string Id => "5f005a27-9838-4c09-9785-55ce49963c97"; - public override string Name => "list_workspaces"; + public override string Name => "list-workspaces"; public override string Title => "List OneLake Workspaces"; public override string Description => "Lists all Fabric workspaces accessible via OneLake data plane API. Use this when the user needs to view available workspaces or select a workspace for data operations. Returns workspace names and IDs."; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/FabricOneLakeSetup.cs b/tools/Fabric.Mcp.Tools.OneLake/src/FabricOneLakeSetup.cs index b6688f3749..09774b1413 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/FabricOneLakeSetup.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/FabricOneLakeSetup.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Fabric.Mcp.Tools.OneLake.Commands; using Fabric.Mcp.Tools.OneLake.Commands.File; using Fabric.Mcp.Tools.OneLake.Commands.Item; using Fabric.Mcp.Tools.OneLake.Commands.Table; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/BlobDownloadOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/BlobDownloadOptions.cs index cc72bedc53..676d7f2930 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/BlobDownloadOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/BlobDownloadOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.IO; - namespace Fabric.Mcp.Tools.OneLake.Models; /// diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/OneLakeJsonContext.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/OneLakeJsonContext.cs index 7c336dfddd..99680216b6 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/OneLakeJsonContext.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/OneLakeJsonContext.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Collections.Generic; using System.Text.Json.Serialization; using Fabric.Mcp.Tools.OneLake.Commands.File; using Fabric.Mcp.Tools.OneLake.Commands.Item; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableConfigurationResult.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableConfigurationResult.cs index 8aa81d0d10..e7990d4477 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableConfigurationResult.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableConfigurationResult.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; namespace Fabric.Mcp.Tools.OneLake.Models; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableGetResult.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableGetResult.cs index e2a3cb2e2c..2811970345 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableGetResult.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableGetResult.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; namespace Fabric.Mcp.Tools.OneLake.Models; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableListResult.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableListResult.cs index ed63cfb33e..53e80d1177 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableListResult.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableListResult.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; namespace Fabric.Mcp.Tools.OneLake.Models; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceGetResult.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceGetResult.cs index 388f8629f2..a362780d9c 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceGetResult.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceGetResult.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; namespace Fabric.Mcp.Tools.OneLake.Models; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceListResult.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceListResult.cs index 03bc77aa83..25abc99c4a 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceListResult.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Models/TableNamespaceListResult.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Text.Json; using System.Text.Json.Serialization; namespace Fabric.Mcp.Tools.OneLake.Models; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseItemOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseItemOptions.cs new file mode 100644 index 0000000000..ecdf06b664 --- /dev/null +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseItemOptions.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Fabric.Mcp.Tools.OneLake.Options; + +public class BaseItemOptions : BaseWorkspaceOptions +{ + public string? ItemId { get; set; } +} diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableConfigGetOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseWorkspaceOptions.cs similarity index 52% rename from tools/Fabric.Mcp.Tools.OneLake/src/Options/TableConfigGetOptions.cs rename to tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseWorkspaceOptions.cs index c3c06cfb3d..775e825830 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableConfigGetOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BaseWorkspaceOptions.cs @@ -5,10 +5,7 @@ namespace Fabric.Mcp.Tools.OneLake.Options; -public sealed class TableConfigGetOptions : GlobalOptions +public class BaseWorkspaceOptions : GlobalOptions { public string? WorkspaceId { get; set; } - public string? Workspace { get; set; } - public string? ItemId { get; set; } - public string? Item { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/BlobListOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BlobListOptions.cs index 543f14289a..7cd0844053 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/BlobListOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/BlobListOptions.cs @@ -1,14 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Options; - namespace Fabric.Mcp.Tools.OneLake.Options; -public class BlobListOptions : GlobalOptions +public class BlobListOptions : BaseItemOptions { - public string? WorkspaceId { get; set; } - public string? ItemId { get; set; } public string? Path { get; set; } public bool Recursive { get; set; } public string? Format { get; set; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs index d1f1576b3c..32f66c12a1 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.CommandLine; - namespace Fabric.Mcp.Tools.OneLake.Options; public static class FabricOptionDefinitions diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/OneLakeOptionDefinitions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/OneLakeOptionDefinitions.cs index a7d6cf3516..1c25fe76be 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/OneLakeOptionDefinitions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/OneLakeOptionDefinitions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.CommandLine; - namespace Fabric.Mcp.Tools.OneLake.Options; public static class OneLakeOptionDefinitions diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/PathListOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/PathListOptions.cs index 2548c64244..5d45b27c00 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/PathListOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/PathListOptions.cs @@ -1,14 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Options; - namespace Fabric.Mcp.Tools.OneLake.Options; -public class PathListOptions : GlobalOptions +public class PathListOptions : BaseItemOptions { - public string? WorkspaceId { get; set; } - public string? ItemId { get; set; } public string? Path { get; set; } public bool Recursive { get; set; } public string? Format { get; set; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableGetOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableGetOptions.cs index 59d3454b8e..0afb4aa1a5 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableGetOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableGetOptions.cs @@ -1,16 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Options; - namespace Fabric.Mcp.Tools.OneLake.Options; -public sealed class TableGetOptions : GlobalOptions +public sealed class TableGetOptions : BaseItemOptions { - public string? WorkspaceId { get; set; } - public string? Workspace { get; set; } - public string? ItemId { get; set; } - public string? Item { get; set; } public string? Namespace { get; set; } public string? Table { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableListOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableListOptions.cs index eb96ea1a99..1e159d7779 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableListOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableListOptions.cs @@ -1,15 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Options; - namespace Fabric.Mcp.Tools.OneLake.Options; -public sealed class TableListOptions : GlobalOptions +public sealed class TableListOptions : BaseItemOptions { - public string? WorkspaceId { get; set; } - public string? Workspace { get; set; } - public string? ItemId { get; set; } - public string? Item { get; set; } public string? Namespace { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceGetOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceGetOptions.cs index d1184334c9..162f0ce06e 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceGetOptions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceGetOptions.cs @@ -1,15 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Azure.Mcp.Core.Options; - namespace Fabric.Mcp.Tools.OneLake.Options; -public sealed class TableNamespaceGetOptions : GlobalOptions +public sealed class TableNamespaceGetOptions : BaseItemOptions { - public string? WorkspaceId { get; set; } - public string? Workspace { get; set; } - public string? ItemId { get; set; } - public string? Item { get; set; } public string? Namespace { get; set; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceListOptions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceListOptions.cs deleted file mode 100644 index a2468cc3a6..0000000000 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/TableNamespaceListOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Azure.Mcp.Core.Options; - -namespace Fabric.Mcp.Tools.OneLake.Options; - -public sealed class TableNamespaceListOptions : GlobalOptions -{ - public string? WorkspaceId { get; set; } - public string? Workspace { get; set; } - public string? ItemId { get; set; } - public string? Item { get; set; } -} diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Prompts/OneLakePrompts.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Prompts/OneLakePrompts.cs index d90481c928..a5b05f3312 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Prompts/OneLakePrompts.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Prompts/OneLakePrompts.cs @@ -20,7 +20,7 @@ public static ChatMessage[] List( { var header = $@"You will list items in OneLake: -- ALWAYS call tool 'onelake_list_items' with: workspace, lakehouse, path. +- ALWAYS call tool 'onelake_list-items' with: workspace, lakehouse, path. - Use paging: set maxResults (<=100) and iterate cursors if provided. - Do NOT assume paths exist; handle 404s gracefully."; diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Services/OneLakeService.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Services/OneLakeService.cs index babebdeccd..3135ae6821 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Services/OneLakeService.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Services/OneLakeService.cs @@ -1,15 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using System.Net.Http.Headers; using System.Text; -using System.Text.Json; using System.Xml.Linq; using Azure.Core; using Azure.Identity; @@ -79,7 +74,7 @@ public async Task ResolveItemIdentifierAsync(string workspaceId, string private ConcurrentDictionary GetWorkspaceItemCache(string workspaceId) { - return _itemIdentifierCache.GetOrAdd(workspaceId, static _ => new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); + return _itemIdentifierCache.GetOrAdd(workspaceId, static _ => new(StringComparer.OrdinalIgnoreCase)); } private static string NormalizeWorkspaceIdentifier(string workspaceId) @@ -139,7 +134,7 @@ public async Task> ListOneLakeWorkspacesAsync(string? con var propertiesElement = container.Element("Properties"); if (propertiesElement != null) { - workspace.Properties = new WorkspaceProperties(); + workspace.Properties = new(); var lastModifiedString = propertiesElement.Element("Last-Modified")?.Value; if (!string.IsNullOrEmpty(lastModifiedString) && DateTime.TryParse(lastModifiedString, out var lastModified)) { @@ -150,7 +145,7 @@ public async Task> ListOneLakeWorkspacesAsync(string? con var metadataElement = container.Element("Metadata"); if (metadataElement != null) { - workspace.Metadata = new WorkspaceMetadata + workspace.Metadata = new() { RegionalServiceEndpoint = metadataElement.Element("RegionalServiceEndpoint")?.Value, WorkspaceObjectId = metadataElement.Element("WorkspaceObjectId")?.Value, @@ -189,7 +184,7 @@ public async Task CreateItemAsync(string workspaceId, CreateItemReq var url = $"{OneLakeEndpoints.GetFabricApiBaseUrl()}/workspaces/{workspaceId}/items"; var jsonContent = JsonSerializer.Serialize(request, OneLakeJsonContext.Default.CreateItemRequest); var response = await SendFabricApiRequestAsync(HttpMethod.Post, url, jsonContent, null, cancellationToken); - return await JsonSerializer.DeserializeAsync(response, OneLakeJsonContext.Default.OneLakeItem, cancellationToken) ?? new OneLakeItem(); + return await JsonSerializer.DeserializeAsync(response, OneLakeJsonContext.Default.OneLakeItem, cancellationToken) ?? new(); } // Private helper method for internal use @@ -197,7 +192,7 @@ private async Task GetWorkspaceAsync(string workspaceId, Cancellation { var url = $"{OneLakeEndpoints.GetFabricApiBaseUrl()}/workspaces/{workspaceId}"; var response = await SendFabricApiRequestAsync(HttpMethod.Get, url, cancellationToken: cancellationToken); - return await JsonSerializer.DeserializeAsync(response, OneLakeJsonContext.Default.Workspace, cancellationToken) ?? new Workspace(); + return await JsonSerializer.DeserializeAsync(response, OneLakeJsonContext.Default.Workspace, cancellationToken) ?? new(); } // Data Operations (OneLake Data Plane) @@ -207,7 +202,7 @@ public async Task GetFileInfoAsync(string workspaceId, string i var url = $"{OneLakeEndpoints.OneLakeDataPlaneBaseUrl}/{normalizedWorkspaceId}/{normalizedItemId}/Files/{filePath.TrimStart('/')}"; var response = await SendDataPlaneRequestAsync(HttpMethod.Head, url, cancellationToken: cancellationToken); - return new OneLakeFileInfo + return new() { Name = Path.GetFileName(filePath), Path = filePath, @@ -346,7 +341,7 @@ public async Task GetTableConfigurationAsync(string wo using var document = JsonDocument.Parse(rawResponse); var configuration = document.RootElement.Clone(); - return new TableConfigurationResult(normalizedWorkspaceId, normalizedItemIdentifier, configuration, rawResponse); + return new(normalizedWorkspaceId, normalizedItemIdentifier, configuration, rawResponse); } public async Task ListTableNamespacesAsync(string workspaceIdentifier, string itemIdentifier, CancellationToken cancellationToken = default) @@ -376,7 +371,7 @@ public async Task ListTableNamespacesAsync(string work using var document = JsonDocument.Parse(rawResponse); var namespaces = document.RootElement.Clone(); - return new TableNamespaceListResult(normalizedWorkspaceId, normalizedItemIdentifier, namespaces, rawResponse); + return new(normalizedWorkspaceId, normalizedItemIdentifier, namespaces, rawResponse); } public async Task GetTableNamespaceAsync(string workspaceIdentifier, string itemIdentifier, string namespaceName, CancellationToken cancellationToken = default) @@ -418,7 +413,7 @@ public async Task GetTableNamespaceAsync(string workspa using var document = JsonDocument.Parse(rawResponse); var definition = document.RootElement.Clone(); - return new TableNamespaceGetResult(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, definition, rawResponse); + return new(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, definition, rawResponse); } public async Task ListTablesAsync(string workspaceIdentifier, string itemIdentifier, string namespaceName, CancellationToken cancellationToken = default) @@ -460,7 +455,7 @@ public async Task ListTablesAsync(string workspaceIdentifier, s using var document = JsonDocument.Parse(rawResponse); var tables = document.RootElement.Clone(); - return new TableListResult(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, tables, rawResponse); + return new(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, tables, rawResponse); } public async Task GetTableAsync(string workspaceIdentifier, string itemIdentifier, string namespaceName, string tableName, CancellationToken cancellationToken = default) @@ -515,7 +510,7 @@ public async Task GetTableAsync(string workspaceIdentifier, stri using var document = JsonDocument.Parse(rawResponse); var tableDefinition = document.RootElement.Clone(); - return new TableGetResult(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, trimmedTableName, tableDefinition, rawResponse); + return new(normalizedWorkspaceId, normalizedItemIdentifier, trimmedNamespace, trimmedTableName, tableDefinition, rawResponse); } private List ParseBlobListResponse(string xmlContent) @@ -547,7 +542,7 @@ private List ParseBlobListResponse(string xmlContent) // Use ResourceType to determine if this is a directory var isDirectory = string.Equals(resourceType, "directory", StringComparison.OrdinalIgnoreCase); - files.Add(new OneLakeFileInfo + files.Add(new() { Name = Path.GetFileName(fileName), Path = fileName, @@ -566,7 +561,7 @@ private List ParseBlobListResponse(string xmlContent) if (nameElement?.Value != null) { var dirName = nameElement.Value.TrimEnd('/'); - files.Add(new OneLakeFileInfo + files.Add(new() { Name = Path.GetFileName(dirName), Path = dirName, @@ -621,7 +616,7 @@ public async Task> ListPathIntelligentAsync(string workspac return allItems.OrderBy(f => f.Type == "directory" ? 0 : 1).ThenBy(f => f.Name).ToList(); } - private List ParsePathListResponse(string jsonContent) + private static List ParsePathListResponse(string jsonContent) { var fileSystemItems = new List(); @@ -751,7 +746,7 @@ public async Task> ListPathAsync(string workspaceId, string return fileSystemItems.OrderBy(f => f.Type == "directory" ? 0 : 1).ThenBy(f => f.Name).ToList(); } - private List BuildHierarchicalStructure(List flatItems, string basePath) + private static List BuildHierarchicalStructure(List flatItems, string basePath) { var root = new List(); var pathPrefix = basePath.TrimEnd('/') + "/"; @@ -835,7 +830,7 @@ public async Task> ListOneLakeItemsAsync(string workspa } // Option 2: Try (fallback for regular blobs) - if (!items.Any()) + if (items.Count == 0) { var blobs = doc.Root?.Element("Blobs")?.Elements("Blob"); if (blobs != null && blobs.Any()) @@ -845,7 +840,7 @@ public async Task> ListOneLakeItemsAsync(string workspa } // Option 2: Try (like workspace listing) - if (!items.Any()) + if (items.Count == 0) { var containers = doc.Root?.Element("Containers")?.Elements("Container"); if (containers != null && containers.Any()) @@ -855,7 +850,7 @@ public async Task> ListOneLakeItemsAsync(string workspa } // Option 3: Try direct children of root element - if (!items.Any()) + if (items.Count == 0) { var directElements = doc.Root?.Elements().Where(e => e.Name != "NextMarker" && e.Name != "MaxResults"); if (directElements != null && directElements.Any()) @@ -1114,7 +1109,7 @@ private static List ParseBlobPrefixElements(IEnumerable b var metadataElement = blobPrefix.Element("Metadata"); if (metadataElement != null) { - item.Metadata = new OneLakeItemMetadata + item.Metadata = new() { ArtifactId = metadataElement.Element("ArtifactId")?.Value, ArtifactPortalUrl = metadataElement.Element("ArtifactPortalUrl")?.Value @@ -1129,7 +1124,7 @@ private static List ParseBlobPrefixElements(IEnumerable b } else if (propertiesElement != null) { - item.Metadata = new OneLakeItemMetadata + item.Metadata = new() { ResourceType = propertiesElement.Element("ResourceType")?.Value, BlobType = propertiesElement.Element("BlobType")?.Value @@ -1286,17 +1281,17 @@ public async Task WriteFileAsync(string workspaceId, string itemId, string fileP // Upload content url += "?action=append&position=0"; - using var uploadRequest = new HttpRequestMessage(new HttpMethod("PATCH"), url) + using var uploadRequest = new HttpRequestMessage(HttpMethod.Patch, url) { Content = new StreamContent(content) }; - uploadRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + uploadRequest.Content.Headers.ContentType = new("application/octet-stream"); await SendDataPlaneRequestAsync(uploadRequest, cancellationToken: cancellationToken); // Flush/commit url = url.Replace("action=append&position=0", $"action=flush&position={content.Length}"); - using var flushRequest = new HttpRequestMessage(new HttpMethod("PATCH"), url); + using var flushRequest = new HttpRequestMessage(HttpMethod.Patch, url); await SendDataPlaneRequestAsync(flushRequest, cancellationToken: cancellationToken); } @@ -1318,7 +1313,7 @@ public async Task PutBlobAsync(string workspaceId, string itemId, }; request.Headers.Add("x-ms-blob-type", "BlockBlob"); - request.Content.Headers.ContentType = new MediaTypeHeaderValue(string.IsNullOrWhiteSpace(contentType) ? "application/octet-stream" : contentType); + request.Content.Headers.ContentType = new(string.IsNullOrWhiteSpace(contentType) ? "application/octet-stream" : contentType); request.Content.Headers.ContentLength = contentLength; if (!overwrite) @@ -1366,7 +1361,7 @@ public async Task PutBlobAsync(string workspaceId, string itemId, } } - return new BlobPutResult( + return new( normalizedWorkspaceId, normalizedItemId, blobPath, @@ -1541,7 +1536,7 @@ private async Task DownloadBlobAsync( var clientRequestId = GetHeaderValue(response.Headers, "x-ms-client-request-id"); var rootActivityId = GetHeaderValue(response.Headers, "x-ms-root-activity-id"); - return new BlobGetResult( + return new( normalizedWorkspaceId, normalizedItemId, path, @@ -1585,7 +1580,7 @@ public async Task DeleteBlobAsync(string workspaceId, string i var clientRequestId = GetHeaderValue(response.Headers, "x-ms-client-request-id"); var rootActivityId = GetHeaderValue(response.Headers, "x-ms-root-activity-id"); - return new BlobDeleteResult( + return new( normalizedWorkspaceId, normalizedItemId, blobPath, @@ -1637,11 +1632,11 @@ public async Task CreateDirectoryAsync(string workspaceId, string itemId, string // Private helper methods private async Task SendFabricApiRequestAsync(HttpMethod method, string url, string? jsonContent = null, string? tenant = null, CancellationToken cancellationToken = default) { - var tokenContext = new TokenRequestContext(new[] { OneLakeEndpoints.GetFabricScope() }); + var tokenContext = new TokenRequestContext([OneLakeEndpoints.GetFabricScope()]); var token = await _credential.GetTokenAsync(tokenContext, cancellationToken); using var request = new HttpRequestMessage(method, url); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); + request.Headers.Authorization = new("Bearer", token.Token); ApplyUserAgent(request); if (!string.IsNullOrEmpty(jsonContent)) @@ -1657,11 +1652,11 @@ private async Task SendFabricApiRequestAsync(HttpMethod method, string u private async Task SendOneLakeApiRequestAsync(HttpMethod method, string url, string? jsonContent = null, CancellationToken cancellationToken = default) { - var tokenContext = new Azure.Core.TokenRequestContext(new[] { OneLakeEndpoints.StorageScope }); + var tokenContext = new TokenRequestContext([OneLakeEndpoints.StorageScope]); var token = await _credential.GetTokenAsync(tokenContext, cancellationToken); using var request = new HttpRequestMessage(method, url); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); + request.Headers.Authorization = new("Bearer", token.Token); request.Headers.Add("x-ms-version", "2023-11-03"); ApplyUserAgent(request); @@ -1684,10 +1679,10 @@ private async Task SendDataPlaneRequestAsync(HttpMethod met private async Task SendDataPlaneRequestAsync(HttpRequestMessage request, string? tenant = null, CancellationToken cancellationToken = default) { - var tokenContext = new TokenRequestContext(new[] { OneLakeEndpoints.StorageScope }); + var tokenContext = new TokenRequestContext([OneLakeEndpoints.StorageScope]); var token = await _credential.GetTokenAsync(tokenContext, cancellationToken); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); + request.Headers.Authorization = new("Bearer", token.Token); request.Headers.Add("x-ms-version", "2023-11-03"); request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R")); ApplyUserAgent(request); diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobGetCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobGetCommandTests.cs index de18fa548e..22b46432a4 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobGetCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobGetCommandTests.cs @@ -4,6 +4,7 @@ using System.Net; using System.Text; using System.Text.Json; +using Fabric.Mcp.Tools.OneLake.Commands.File; using Fabric.Mcp.Tools.OneLake.Models; using Fabric.Mcp.Tools.OneLake.Services; using Microsoft.Extensions.Logging.Abstractions; @@ -22,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new BlobGetCommand(NullLogger.Instance, service); - Assert.Equal("download_file", command.Name); + Assert.Equal("download-file", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobPutCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobPutCommandTests.cs index 6874ca096a..28aeb04ba0 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobPutCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/BlobPutCommandTests.cs @@ -28,7 +28,7 @@ public void Constructor_InitializesCommandCorrectly() var oneLakeService = Substitute.For(); var command = new BlobPutCommand(NullLogger.Instance, oneLakeService); - Assert.Equal("upload_file", command.Name); + Assert.Equal("upload-file", command.Name); Assert.Contains("Uploads a file to OneLake storage", command.Description, StringComparison.OrdinalIgnoreCase); Assert.False(command.Metadata.ReadOnly); Assert.True(command.Metadata.Destructive); @@ -43,7 +43,7 @@ public void GetCommand_ReturnsValidCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("upload_file", systemCommand.Name); + Assert.Equal("upload-file", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryCreateCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryCreateCommandTests.cs index 9ce4e757f8..4bdf2438fc 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryCreateCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryCreateCommandTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new DirectoryCreateCommand(logger, oneLakeService); // Assert - Assert.Equal("create_directory", command.Name); + Assert.Equal("create-directory", command.Name); Assert.Equal("Create OneLake Directory", command.Title); Assert.Contains("Creates a directory in OneLake storage", command.Description); Assert.False(command.Metadata.ReadOnly); @@ -42,7 +42,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("create_directory", systemCommand.Name); + Assert.Equal("create-directory", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryDeleteCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryDeleteCommandTests.cs index 81bc2432c4..f52c46e183 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryDeleteCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/DirectoryDeleteCommandTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new DirectoryDeleteCommand(logger, oneLakeService); // Assert - Assert.Equal("delete_directory", command.Name); + Assert.Equal("delete-directory", command.Name); Assert.Equal("Delete OneLake Directory", command.Title); Assert.Contains("Deletes a directory from OneLake storage", command.Description); Assert.False(command.Metadata.ReadOnly); @@ -42,7 +42,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("delete_directory", systemCommand.Name); + Assert.Equal("delete-directory", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemDataListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemDataListCommandTests.cs index d1564e21c5..b2194fe5b0 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemDataListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemDataListCommandTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new OneLakeItemDataListCommand(logger, oneLakeService); // Assert - Assert.Equal("list_items_dfs", command.Name); + Assert.Equal("list-items-dfs", command.Name); Assert.Equal("List OneLake Items (Data API)", command.Title); Assert.Contains("OneLake DFS", command.Description); Assert.True(command.Metadata.ReadOnly); @@ -42,7 +42,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("list_items_dfs", systemCommand.Name); + Assert.Equal("list-items-dfs", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemListCommandTests.cs index ad0d82e859..4df0a9d068 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeItemListCommandTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new OneLakeItemListCommand(logger, oneLakeService); // Assert - Assert.Equal("list_items", command.Name); + Assert.Equal("list-items", command.Name); Assert.Equal("List OneLake Items", command.Title); Assert.Contains("Lists OneLake items in a Fabric workspace", command.Description); Assert.True(command.Metadata.ReadOnly); @@ -42,7 +42,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("list_items", systemCommand.Name); + Assert.Equal("list-items", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeWorkspaceListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeWorkspaceListCommandTests.cs index 24fbcea64b..67b56a66e0 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeWorkspaceListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/OneLakeWorkspaceListCommandTests.cs @@ -21,7 +21,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new OneLakeWorkspaceListCommand(logger, oneLakeService); // Assert - Assert.Equal("list_workspaces", command.Name); + Assert.Equal("list-workspaces", command.Name); Assert.Equal("List OneLake Workspaces", command.Title); Assert.Contains("Lists all Fabric workspaces accessible via OneLake", command.Description); Assert.True(command.Metadata.ReadOnly); @@ -42,7 +42,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("list_workspaces", systemCommand.Name); + Assert.Equal("list-workspaces", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/PathListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/PathListCommandTests.cs index 7d2dbf540c..58d80d876a 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/PathListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/PathListCommandTests.cs @@ -18,7 +18,7 @@ public void Constructor_InitializesCommandCorrectly() var command = new PathListCommand(logger); // Assert - Assert.Equal("list_files", command.Name); + Assert.Equal("list-files", command.Name); Assert.Equal("List OneLake Path Structure", command.Title); Assert.Contains("List files and directories", command.Description); Assert.True(command.Metadata.ReadOnly); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsValidCommand() // Assert Assert.NotNull(systemCommand); - Assert.Equal("list_files", systemCommand.Name); + Assert.Equal("list-files", systemCommand.Name); Assert.NotNull(systemCommand.Description); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableConfigGetCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableConfigGetCommandTests.cs index b28a6acf42..1a3cda20a4 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableConfigGetCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableConfigGetCommandTests.cs @@ -23,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new TableConfigGetCommand(NullLogger.Instance, service); - Assert.Equal("get_table_config", command.Name); + Assert.Equal("get-table-config", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsConfiguredCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("get_table_config", systemCommand.Name); + Assert.Equal("get-table-config", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableGetCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableGetCommandTests.cs index 46a40c27cb..63728081a5 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableGetCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableGetCommandTests.cs @@ -23,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new TableGetCommand(NullLogger.Instance, service); - Assert.Equal("get_table", command.Name); + Assert.Equal("get-table", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsConfiguredCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("get_table", systemCommand.Name); + Assert.Equal("get-table", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableListCommandTests.cs index 644dc5e339..c353fad367 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableListCommandTests.cs @@ -23,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new TableListCommand(NullLogger.Instance, service); - Assert.Equal("list_tables", command.Name); + Assert.Equal("list-tables", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsConfiguredCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("list_tables", systemCommand.Name); + Assert.Equal("list-tables", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceGetCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceGetCommandTests.cs index 7f82fdccc1..e59571425d 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceGetCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceGetCommandTests.cs @@ -23,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new TableNamespaceGetCommand(NullLogger.Instance, service); - Assert.Equal("get_table_namespace", command.Name); + Assert.Equal("get-table-namespace", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsConfiguredCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("get_table_namespace", systemCommand.Name); + Assert.Equal("get-table-namespace", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceListCommandTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceListCommandTests.cs index 30f6ad38e1..b43cd82c2f 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceListCommandTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/Commands/Table/TableNamespaceListCommandTests.cs @@ -23,7 +23,7 @@ public void Constructor_InitializesMetadata() var service = Substitute.For(); var command = new TableNamespaceListCommand(NullLogger.Instance, service); - Assert.Equal("list_table_namespaces", command.Name); + Assert.Equal("list-table-namespaces", command.Name); Assert.True(command.Metadata.ReadOnly); Assert.True(command.Metadata.Idempotent); Assert.False(command.Metadata.Destructive); @@ -38,7 +38,7 @@ public void GetCommand_ReturnsConfiguredCommand() var systemCommand = command.GetCommand(); Assert.NotNull(systemCommand); - Assert.Equal("list_table_namespaces", systemCommand.Name); + Assert.Equal("list-table-namespaces", systemCommand.Name); Assert.NotEmpty(systemCommand.Options); } diff --git a/tools/Fabric.Mcp.Tools.OneLake/tests/FabricOneLakeSetupTests.cs b/tools/Fabric.Mcp.Tools.OneLake/tests/FabricOneLakeSetupTests.cs index ffda4ca610..a272fc8d7a 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/tests/FabricOneLakeSetupTests.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/tests/FabricOneLakeSetupTests.cs @@ -47,21 +47,21 @@ public void RegisterCommands_RegistersAllOneLakeCommands() var rootGroup = setup.RegisterCommands(provider); // Assert - flat structure with verb_object naming - Assert.True(rootGroup.Commands.ContainsKey("list_workspaces"), "Should have list_workspaces command"); - Assert.True(rootGroup.Commands.ContainsKey("list_items"), "Should have list_items command"); - Assert.True(rootGroup.Commands.ContainsKey("list_items_dfs"), "Should have list_items_dfs command"); - Assert.True(rootGroup.Commands.ContainsKey("list_files"), "Should have list_files command"); - Assert.True(rootGroup.Commands.ContainsKey("download_file"), "Should have download_file command"); - Assert.True(rootGroup.Commands.ContainsKey("upload_file"), "Should have upload_file command"); - Assert.True(rootGroup.Commands.ContainsKey("delete_file"), "Should have delete_file command"); - Assert.True(rootGroup.Commands.ContainsKey("create_directory"), "Should have create_directory command"); - Assert.True(rootGroup.Commands.ContainsKey("delete_directory"), "Should have delete_directory command"); + Assert.True(rootGroup.Commands.ContainsKey("list-workspaces"), "Should have list-workspaces command"); + Assert.True(rootGroup.Commands.ContainsKey("list-items"), "Should have list-items command"); + Assert.True(rootGroup.Commands.ContainsKey("list-items-dfs"), "Should have list-items-dfs command"); + Assert.True(rootGroup.Commands.ContainsKey("list-files"), "Should have list-files command"); + Assert.True(rootGroup.Commands.ContainsKey("download-file"), "Should have download-file command"); + Assert.True(rootGroup.Commands.ContainsKey("upload-file"), "Should have upload-file command"); + Assert.True(rootGroup.Commands.ContainsKey("delete-file"), "Should have delete-file command"); + Assert.True(rootGroup.Commands.ContainsKey("create-directory"), "Should have create-directory command"); + Assert.True(rootGroup.Commands.ContainsKey("delete-directory"), "Should have delete-directory command"); // Table commands - Assert.True(rootGroup.Commands.ContainsKey("get_table_config"), "Should have get_table_config command"); - Assert.True(rootGroup.Commands.ContainsKey("list_tables"), "Should have list_tables command"); - Assert.True(rootGroup.Commands.ContainsKey("get_table"), "Should have get_table command"); - Assert.True(rootGroup.Commands.ContainsKey("list_table_namespaces"), "Should have list_table_namespaces command"); - Assert.True(rootGroup.Commands.ContainsKey("get_table_namespace"), "Should have get_table_namespace command"); + Assert.True(rootGroup.Commands.ContainsKey("get-table-config"), "Should have get-table-config command"); + Assert.True(rootGroup.Commands.ContainsKey("list-tables"), "Should have list-tables command"); + Assert.True(rootGroup.Commands.ContainsKey("get-table"), "Should have get-table command"); + Assert.True(rootGroup.Commands.ContainsKey("list-table-namespaces"), "Should have list-table-namespaces command"); + Assert.True(rootGroup.Commands.ContainsKey("get-table-namespace"), "Should have get-table-namespace command"); } } From 33387ea5ab2f71aa6908af527f42e3868650c982 Mon Sep 17 00:00:00 2001 From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:37:42 -0400 Subject: [PATCH 2/2] Switch to using Option aliases --- .../src/Commands/BaseItemCommand.cs | 20 ++----------- .../src/Commands/BaseWorkspaceCommand.cs | 20 ++----------- .../src/Commands/Table/TableGetCommand.cs | 6 ++-- .../src/Commands/Table/TableListCommand.cs | 4 +-- .../Table/TableNamespaceGetCommand.cs | 4 +-- .../src/Options/FabricOptionDefinitions.cs | 30 ++++--------------- 6 files changed, 14 insertions(+), 70 deletions(-) diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs index 3a932bd6da..a95ba0f567 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseItemCommand.cs @@ -6,7 +6,6 @@ using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Options; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands; @@ -21,28 +20,13 @@ protected BaseItemCommand() protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.ItemId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Item.AsOptional()); - command.Validators.Add(result => - { - var itemId = result.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = result.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - - if (string.IsNullOrWhiteSpace(item) && string.IsNullOrWhiteSpace(itemId)) - { - result.AddError("Item identifier is required. Provide --item or --item-id."); - } - }); + command.Options.Add(FabricOptionDefinitions.ItemId); } protected override TOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var itemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); - var item = parseResult.GetValueOrDefault(FabricOptionDefinitions.Item.Name); - options.ItemId = !string.IsNullOrWhiteSpace(itemId) - ? itemId! - : item ?? string.Empty; + options.ItemId = parseResult.GetValueOrDefault(FabricOptionDefinitions.ItemId.Name); return options; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs index 6f198591c6..d2ca5b7fa2 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/BaseWorkspaceCommand.cs @@ -6,7 +6,6 @@ using Azure.Mcp.Core.Extensions; using Fabric.Mcp.Tools.OneLake.Options; using Microsoft.Mcp.Core.Extensions; -using Microsoft.Mcp.Core.Models.Option; namespace Fabric.Mcp.Tools.OneLake.Commands; @@ -21,28 +20,13 @@ protected BaseWorkspaceCommand() protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.WorkspaceId.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Workspace.AsOptional()); - command.Validators.Add(result => - { - var workspaceId = result.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspace = result.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - - if (string.IsNullOrWhiteSpace(workspaceId) && string.IsNullOrWhiteSpace(workspace)) - { - result.AddError("Workspace identifier is required. Provide --workspace or --workspace-id."); - } - }); + command.Options.Add(FabricOptionDefinitions.WorkspaceId); } protected override TOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); - var workspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); - var workspaceName = parseResult.GetValueOrDefault(FabricOptionDefinitions.Workspace.Name); - options.WorkspaceId = !string.IsNullOrWhiteSpace(workspaceId) - ? workspaceId! - : workspaceName ?? string.Empty; + options.WorkspaceId = parseResult.GetValueOrDefault(FabricOptionDefinitions.WorkspaceId.Name); return options; } } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs index efa6dfd469..87cfc711ae 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableGetCommand.cs @@ -37,16 +37,14 @@ public sealed class TableGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Table.AsRequired()); + command.Options.Add(FabricOptionDefinitions.Namespace); + command.Options.Add(FabricOptionDefinitions.Table); } protected override TableGetOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); - options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); options.Table = parseResult.GetValueOrDefault(FabricOptionDefinitions.Table.Name); return options; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs index b2e28e8c6b..4b477ac110 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableListCommand.cs @@ -37,15 +37,13 @@ public sealed class TableListCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); + command.Options.Add(FabricOptionDefinitions.Namespace); } protected override TableListOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); - options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); return options; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs index 1cc4003a20..ccc95081e3 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Commands/Table/TableNamespaceGetCommand.cs @@ -37,15 +37,13 @@ public sealed class TableNamespaceGetCommand( protected override void RegisterOptions(Command command) { base.RegisterOptions(command); - command.Options.Add(FabricOptionDefinitions.Namespace.AsOptional()); - command.Options.Add(FabricOptionDefinitions.Schema.AsOptional()); + command.Options.Add(FabricOptionDefinitions.Namespace); } protected override TableNamespaceGetOptions BindOptions(ParseResult parseResult) { var options = base.BindOptions(parseResult); options.Namespace = parseResult.GetValueOrDefault(FabricOptionDefinitions.Namespace.Name); - options.Namespace ??= parseResult.GetValueOrDefault(FabricOptionDefinitions.Schema.Name); return options; } diff --git a/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs b/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs index 32f66c12a1..b3123ed8eb 100644 --- a/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs +++ b/tools/Fabric.Mcp.Tools.OneLake/src/Options/FabricOptionDefinitions.cs @@ -8,31 +8,19 @@ public static class FabricOptionDefinitions // Workspace options public const string WorkspaceName = "workspace"; - public static readonly Option Workspace = new($"--{WorkspaceName}") - { - Description = "The name or ID of the Microsoft Fabric workspace.", - Required = true - }; - public const string WorkspaceIdName = "workspace-id"; - public static readonly Option WorkspaceId = new($"--{WorkspaceIdName}") + public static readonly Option WorkspaceId = new($"--{WorkspaceIdName}", $"--{WorkspaceName}") { - Description = "The ID of the Microsoft Fabric workspace.", + Description = "The name or ID of the Microsoft Fabric workspace.", Required = true }; // Item options public const string ItemName = "item"; - public static readonly Option Item = new($"--{ItemName}") - { - Description = "The name or ID of the Fabric item. When using friendly names, MUST include the item type suffix (e.g., 'ItemName.Lakehouse', 'ItemName.Warehouse').", - Required = false - }; - public const string ItemIdName = "item-id"; - public static readonly Option ItemId = new($"--{ItemIdName}") + public static readonly Option ItemId = new($"--{ItemIdName}", $"--{ItemName}") { - Description = "The ID of the Fabric item.", + Description = "The name or ID of the Fabric item. When using friendly names, MUST include the item type suffix (e.g., 'ItemName.Lakehouse', 'ItemName.Warehouse').", Required = true }; @@ -157,19 +145,13 @@ public static class FabricOptionDefinitions // Table namespace options public const string NamespaceName = "namespace"; - public static readonly Option Namespace = new($"--{NamespaceName}") + public const string SchemaName = "schema"; + public static readonly Option Namespace = new($"--{NamespaceName}", $"--{SchemaName}") { Description = "The table namespace (schema) to inspect within the OneLake table API.", Required = true }; - public const string SchemaName = "schema"; - public static readonly Option Schema = new($"--{SchemaName}") - { - Description = "Alias for --namespace when specifying table schemas in the OneLake table API.", - Required = false - }; - public const string TableName = "table"; public static readonly Option Table = new($"--{TableName}") {