Skip to content
Merged
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
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ regex = { version = "1.12.3" }
# registry, dsc-lib, dsc-lib-registry, dsctest
registry = { version = "1.3" }
# dsc
rmcp = { version = "1.5.0" }
rmcp = { version = "1.7.0" }
# dsc_lib
rt-format = { version = "0.3" }
# dsc, dsc-lib, dsc-bicep-ext, dscecho, registry, dsc-lib-registry, runcommandonset, sshdconfig
Expand Down
8 changes: 7 additions & 1 deletion dsc/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use dsc_lib::dscresources::command_resource::TraceLevel;
use dsc_lib::progress::ProgressFormat;
use dsc_lib::types::{FullyQualifiedTypeName, ResourceVersionReq, TypeNameFilter};
use rust_i18n::t;
use schemars::JsonSchema;
use serde::Deserialize;

#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
Expand Down Expand Up @@ -296,9 +297,11 @@ pub enum ResourceSubCommand {
},
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
#[derive(Debug, Deserialize, Clone, Copy, JsonSchema, PartialEq, Eq, ValueEnum)]
pub enum SchemaType {
AdaptedDscResourceManifest,
Configuration,
ConfigurationExportResult,
ConfigurationGetResult,
ConfigurationSetResult,
ConfigurationTestResult,
Expand All @@ -311,6 +314,9 @@ pub enum SchemaType {
ManifestList,
ResolveResult,
Resource,
ResourceGetResult,
ResourceSetResult,
ResourceTestResult,
ResourceManifest,
RestartRequired,
SetResult,
Expand Down
1 change: 1 addition & 0 deletions dsc/src/mcp/mcp_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl McpServer {
+ Self::list_dsc_functions_router()
+ Self::list_dsc_resources_router()
+ Self::show_dsc_resource_router()
+ Self::show_dsc_schema_router()
}
}
}
Expand Down
1 change: 1 addition & 0 deletions dsc/src/mcp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod list_dsc_functions;
pub mod list_dsc_resources;
pub mod mcp_server;
pub mod show_dsc_resource;
pub mod show_dsc_schema;

/// This function initializes and starts the MCP server, handling any errors that may occur.
///
Expand Down
7 changes: 6 additions & 1 deletion dsc/src/mcp/show_dsc_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ use dsc_lib::{
};
use rmcp::{ErrorData as McpError, Json, tool, tool_router, handler::server::wrapper::Parameters};
use rust_i18n::t;
use schemars::JsonSchema;
use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::task;

fn nullable_json_object_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
json_schema!({"oneOf": [{"type": "null"}, {"type": "object"}]})
}

#[derive(Serialize, JsonSchema)]
pub struct DscResource {
/// The namespaced name of the resource.
Expand All @@ -35,6 +39,7 @@ pub struct DscResource {
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(schema_with = "nullable_json_object_schema")]
pub schema: Option<Value>,
}

Expand Down
47 changes: 47 additions & 0 deletions dsc/src/mcp/show_dsc_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::{args::SchemaType, mcp::mcp_server::McpServer, util};
use rmcp::{ErrorData as McpError, Json, tool, tool_router, handler::server::wrapper::Parameters};
use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::task;

fn json_object_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
json_schema!({"type": "object"})
}

#[derive(Deserialize, JsonSchema)]
pub struct ShowSchemaRequest {
#[schemars(description = "The schema type to retrieve the JSON schema for.")]
pub r#type: SchemaType,
}

#[derive(Serialize, JsonSchema)]
pub struct ShowSchemaResponse {
#[schemars(schema_with = "json_object_schema")]
pub schema: Value,
}

#[tool_router(router = show_dsc_schema_router, vis = "pub")]
impl McpServer {
#[tool(
description = "Get the JSON schema for a specific part of using DSC, such as a configuration or output from an operation.",
annotations(
title = "Get the JSON schema for a specific part of using DSC",
read_only_hint = true,
destructive_hint = false,
idempotent_hint = true,
open_world_hint = true,
)
)]
pub async fn show_dsc_schema(&self, Parameters(ShowSchemaRequest { r#type }): Parameters<ShowSchemaRequest>) -> Result<Json<ShowSchemaResponse>, McpError> {
let result = task::spawn_blocking(move || {
let schema = util::get_schema(r#type);
Ok(ShowSchemaResponse { schema: schema.as_value().clone() })
}).await.map_err(|e| McpError::internal_error(e.to_string(), None))??;

Ok(Json(result))
}
}
17 changes: 17 additions & 0 deletions dsc/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

use crate::args::{SchemaType, OutputFormat, TraceFormat};
use crate::resolve::Include;
use dsc_lib::configure::config_result::{ConfigurationExportResult, ResourceGetResult, ResourceSetResult};
use dsc_lib::dscresources::adapted_resource_manifest::AdaptedDscResourceManifest;
use dsc_lib::{
configure::{
config_doc::{
Expand Down Expand Up @@ -161,9 +163,15 @@ pub fn add_fields_to_json(json: &str, fields_to_add: &HashMap<String, String>) -
#[must_use]
pub fn get_schema(schema: SchemaType) -> Schema {
match schema {
SchemaType::AdaptedDscResourceManifest => {
schema_for!(AdaptedDscResourceManifest)
},
SchemaType::Configuration => {
schema_for!(Configuration)
},
SchemaType::ConfigurationExportResult => {
schema_for!(ConfigurationExportResult)
},
SchemaType::ConfigurationGetResult => {
schema_for!(ConfigurationGetResult)
},
Expand Down Expand Up @@ -200,6 +208,15 @@ pub fn get_schema(schema: SchemaType) -> Schema {
SchemaType::Resource => {
schema_for!(Resource)
},
SchemaType::ResourceGetResult => {
schema_for!(ResourceGetResult)
},
SchemaType::ResourceSetResult => {
schema_for!(ResourceSetResult)
},
SchemaType::ResourceTestResult => {
schema_for!(ResourceTestResult)
},
SchemaType::ResourceManifest => {
schema_for!(ResourceManifest)
},
Expand Down
23 changes: 23 additions & 0 deletions dsc/tests/dsc_mcp.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Describe 'Tests for MCP server' {
'list_dsc_functions' = $false
'list_dsc_resources' = $false
'show_dsc_resource' = $false
'show_dsc_schema' = $false
}

$response = Send-McpRequest -request $mcpRequest
Expand Down Expand Up @@ -611,4 +612,26 @@ greeting: Hello from YAML parameters
$response.error.code | Should -Be -32600
$response.error.message | Should -Match 'Invalid parameters'
}

It 'Calling show_dsc_schema works' {
$mcpRequest = @{
jsonrpc = "2.0"
id = 19
method = "tools/call"
params = @{
name = "show_dsc_schema"
arguments = @{
type = "AdaptedDscResourceManifest"
}
}
}

$response = Send-McpRequest -request $mcpRequest
$response.id | Should -Be 19
$response.result.structuredContent | Should -Not -BeNullOrEmpty
$response.result.structuredContent.schema | Should -Not -BeNullOrEmpty
$schema = dsc schema --type adapted-dsc-resource-manifest | ConvertFrom-Json -Depth 20
$response.result.structuredContent.schema.'$schema' | Should -Be $schema.'$schema' -Because ($response.result.structuredContent | ConvertTo-Json -Depth 20 | Out-String)
$response.result.structuredContent.schema.title | Should -BeExactly $schema.title -Because ($response.result.structuredContent | ConvertTo-Json -Depth 20 | Out-String)
}
Comment thread
SteveL-MSFT marked this conversation as resolved.
}
Loading