Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
changes:
- section: "Features Added"
description: |
Added five Cosmos DB MCP tools:
- `cosmos_database_container_schema_get`: Infer container schema by sampling documents
- `cosmos_database_container_item_list-recent`: Return the most recently modified documents
- `cosmos_database_container_item_get`: Look up a document by id, with optional partition key for point reads
- `cosmos_database_container_item_text-search`: FullTextContains-based property search
- `cosmos_database_container_item_vector-search`: VectorDistance similarity search with optional Azure OpenAI embedding generation via `--search-text`
51 changes: 51 additions & 0 deletions servers/Azure.Mcp.Server/docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,57 @@ azmcp cosmos database container item query --subscription <subscription> \
--database <database> \
--container <container> \
[--query "SELECT * FROM c"]

# Infer an approximate schema for a Cosmos DB container by sampling documents.
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp cosmos database container schema get --subscription <subscription> \
--account <account> \
--database <database> \
--container <container> \
[--sample-size 10]

# Get the most recently modified documents from a Cosmos DB container.
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp cosmos database container item list-recent --subscription <subscription> \
--account <account> \
--database <database> \
--container <container> \
[--count 10]

# Get a single Cosmos DB document by id (point read when --partition-key is supplied).
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp cosmos database container item get --subscription <subscription> \
--account <account> \
--database <database> \
--container <container> \
--id <id> \
[--partition-key <partition-key>]

# Search Cosmos DB documents where a property contains a phrase (FullTextContains).
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp cosmos database container item text-search --subscription <subscription> \
--account <account> \
--database <database> \
--container <container> \
--property <property> \
--search-phrase <phrase> \
[--count 10]

# Vector similarity search against a Cosmos DB container. Provide --embedding (CSV floats)
# or --search-text plus --openai-endpoint and --embedding-deployment to generate one.
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp cosmos database container item vector-search --subscription <subscription> \
--account <account> \
--database <database> \
--container <container> \
--vector-property <vector-property> \
--select-properties <p1,p2,...> \
[--count 10] \
[--embedding "0.1,0.2,..."] \
[--search-text "free-form text"] \
[--openai-endpoint <endpoint>] \
[--embedding-deployment <deployment>] \
[--embedding-dimensions <n>]
```

### Azure Data Explorer Operations
Expand Down
9 changes: 9 additions & 0 deletions servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,15 @@ This file contains prompts used for end-to-end testing to ensure each tool is in
| cosmos_list | List all the containers in the database <database_name> for the cosmosdb account <account_name> |
| cosmos_list | Show me the containers in the database <database_name> for the cosmosdb account <account_name> |
| cosmos_database_container_item_query | Show me the items that contain the word <search_term> in the container <container_name> in the database <database_name> for the cosmosdb account <account_name> |
| cosmos_database_container_item_get | Get the document with id <document_id> from container <container_name> in database <database_name> of the cosmosdb account <account_name> |
| cosmos_database_container_item_get | Find the document <document_id> in container <container_name> from database <database_name> of the cosmosdb account <account_name> using partition key <partition_key> |
| cosmos_database_container_item_list-recent | Show me the 15 most recent documents in container <container_name> of database <database_name> in cosmosdb account <account_name> |
| cosmos_database_container_item_list-recent | Get the latest documents from <container_name> in <database_name> for cosmosdb account <account_name> |
| cosmos_database_container_item_text-search | Search documents in container <container_name> from database <database_name> of the cosmosdb account <account_name> where <property> contains "<phrase>" |
| cosmos_database_container_item_vector-search | Run a vector search in container <container_name> of database <database_name> for cosmosdb account <account_name> using vector property <vector_property> and the embedding <embedding> |
| cosmos_database_container_item_vector-search | Find documents similar to "<text_to_search>" in container <container_name> using vector property <vector_property> with Azure OpenAI endpoint <endpoint> and deployment <deployment> |
| cosmos_database_container_schema_get | Infer the schema of container <container_name> in database <database_name> for cosmosdb account <account_name> |
| cosmos_database_container_schema_get | Sample <sample_size> documents from container <container_name> in database <database_name> of the cosmosdb account <account_name> and tell me the property names and types |

## Azure Data Explorer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Azure.ResourceManager.CosmosDB" />
<PackageReference Include="Microsoft.HybridRow"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Net;
using Azure.Mcp.Tools.Cosmos.Options;
using Azure.Mcp.Tools.Cosmos.Options.Container;
using Azure.Mcp.Tools.Cosmos.Services;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models;
using Microsoft.Mcp.Core.Models.Command;

namespace Azure.Mcp.Tools.Cosmos.Commands.Container;

[CommandMetadata(
Id = "f1c6a0e2-3d40-4b3f-9a37-2dc1f6cf4a12",
Name = "get",
Title = "Infer Cosmos DB Container Schema",
Description = "Infer an approximate schema for a Cosmos DB container by sampling documents and reporting the top-level properties along with their inferred types and the number of sampled documents in which each appeared.",
Destructive = false,
Idempotent = true,
OpenWorld = false,
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class ContainerSchemaGetCommand(ILogger<ContainerSchemaGetCommand> logger, ICosmosService cosmosService)
: BaseContainerCommand<ContainerSchemaGetOptions>()
{
private readonly ILogger<ContainerSchemaGetCommand> _logger = logger;
private readonly ICosmosService _cosmosService = cosmosService;

protected override void RegisterOptions(Command command)
{
base.RegisterOptions(command);
command.Options.Add(CosmosOptionDefinitions.SampleSize);
command.Validators.Add(result =>
{
var size = result.GetValueOrDefault<int>(CosmosOptionDefinitions.SampleSize.Name);
if (size < 1 || size > 100)
{
result.AddError("--sample-size must be between 1 and 100.");
Comment on lines +41 to +43
}
});
}

protected override ContainerSchemaGetOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.SampleSize = parseResult.GetValueOrDefault<int>(CosmosOptionDefinitions.SampleSize.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

try
{
var schema = await _cosmosService.GetApproximateSchema(
options.Account!,
options.Database!,
options.Container!,
options.SampleSize ?? 10,
options.Subscription!,
options.AuthMethod ?? AuthMethod.Credential,
options.Tenant,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(
new ContainerSchemaGetCommandResult(schema.SampleSize, schema.Properties),
CosmosJsonContext.Default.ContainerSchemaGetCommandResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in {Operation}. Account: {Account}, Database: {Database}, Container: {Container}",
Name, options.Account, options.Database, options.Container);
HandleException(context, ex);
}

return context.Response;
}

protected override string GetErrorMessage(Exception ex) => ex switch
{
CosmosException cosmosEx => cosmosEx.Message,
_ => base.GetErrorMessage(ex)
};

protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch
{
CosmosException cosmosEx => cosmosEx.StatusCode,
_ => base.GetStatusCode(ex)
};

internal record ContainerSchemaGetCommandResult(int SampleSize, IReadOnlyList<Models.SchemaProperty> Properties);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.Mcp.Tools.Cosmos.Commands.Container;
using Azure.Mcp.Tools.Cosmos.Commands.Item;

namespace Azure.Mcp.Tools.Cosmos.Commands;

[JsonSerializable(typeof(CosmosListCommand.CosmosListCommandResult))]
[JsonSerializable(typeof(ItemQueryCommand.ItemQueryCommandResult))]
[JsonSerializable(typeof(ContainerSchemaGetCommand.ContainerSchemaGetCommandResult))]
[JsonSerializable(typeof(ItemListRecentCommand.ItemListRecentCommandResult))]
[JsonSerializable(typeof(ItemGetCommand.ItemGetCommandResult))]
[JsonSerializable(typeof(ItemTextSearchCommand.ItemTextSearchCommandResult))]
[JsonSerializable(typeof(ItemVectorSearchCommand.ItemVectorSearchCommandResult))]
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Net;
using Azure.Mcp.Tools.Cosmos.Options;
using Azure.Mcp.Tools.Cosmos.Options.Item;
using Azure.Mcp.Tools.Cosmos.Services;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models;
using Microsoft.Mcp.Core.Models.Command;

namespace Azure.Mcp.Tools.Cosmos.Commands.Item;

[CommandMetadata(
Id = "d4c1b2a3-9e8f-4d7c-86b5-1a2b3c4d5e6f",
Name = "get",
Title = "Get Cosmos DB Document by Id",
Description = "Find a single Cosmos DB document by its id in the specified database and container. When --partition-key is supplied, an efficient point read is used; otherwise a cross-partition query is executed.",
Destructive = false,
Idempotent = true,
OpenWorld = false,
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class ItemGetCommand(ILogger<ItemGetCommand> logger, ICosmosService cosmosService)
: BaseContainerCommand<ItemGetOptions>()
{
private readonly ILogger<ItemGetCommand> _logger = logger;
private readonly ICosmosService _cosmosService = cosmosService;

protected override void RegisterOptions(Command command)
{
base.RegisterOptions(command);
command.Options.Add(CosmosOptionDefinitions.ItemId);
command.Options.Add(CosmosOptionDefinitions.PartitionKey);
}

protected override ItemGetOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.Id = parseResult.GetValueOrDefault<string>(CosmosOptionDefinitions.ItemId.Name);
options.PartitionKey = parseResult.GetValueOrDefault<string?>(CosmosOptionDefinitions.PartitionKey.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

try
{
var item = await _cosmosService.GetItem(
options.Account!,
options.Database!,
options.Container!,
options.Id!,
options.PartitionKey,
options.Subscription!,
options.AuthMethod ?? AuthMethod.Credential,
options.Tenant,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(
new ItemGetCommandResult(item),
CosmosJsonContext.Default.ItemGetCommandResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in {Operation}. Account: {Account}, Database: {Database}, Container: {Container}",
Name, options.Account, options.Database, options.Container);
HandleException(context, ex);
}

return context.Response;
}

protected override string GetErrorMessage(Exception ex) => ex switch
{
CosmosException cosmosEx => cosmosEx.Message,
_ => base.GetErrorMessage(ex)
};

protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch
{
CosmosException cosmosEx => cosmosEx.StatusCode,
_ => base.GetStatusCode(ex)
};

internal record ItemGetCommandResult(JsonElement? Item);
}
Loading
Loading