Skip to content

Commit 6c162ed

Browse files
authored
Merge pull request #1553 from SteveL-MSFT/mcp-schema
Add `show_dsc_schema()` MCP function
2 parents f500963 + a22eed7 commit 6c162ed

9 files changed

Lines changed: 107 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ regex = { version = "1.12.3" }
201201
# registry, dsc-lib, dsc-lib-registry, dsctest
202202
registry = { version = "1.3" }
203203
# dsc
204-
rmcp = { version = "1.5.0" }
204+
rmcp = { version = "1.7.0" }
205205
# dsc_lib
206206
rt-format = { version = "0.3" }
207207
# dsc, dsc-lib, dsc-bicep-ext, dscecho, registry, dsc-lib-registry, runcommandonset, sshdconfig

dsc/src/args.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use dsc_lib::dscresources::command_resource::TraceLevel;
77
use dsc_lib::progress::ProgressFormat;
88
use dsc_lib::types::{FullyQualifiedTypeName, ResourceVersionReq, TypeNameFilter};
99
use rust_i18n::t;
10+
use schemars::JsonSchema;
1011
use serde::Deserialize;
1112

1213
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
@@ -296,9 +297,11 @@ pub enum ResourceSubCommand {
296297
},
297298
}
298299

299-
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
300+
#[derive(Debug, Deserialize, Clone, Copy, JsonSchema, PartialEq, Eq, ValueEnum)]
300301
pub enum SchemaType {
302+
AdaptedDscResourceManifest,
301303
Configuration,
304+
ConfigurationExportResult,
302305
ConfigurationGetResult,
303306
ConfigurationSetResult,
304307
ConfigurationTestResult,
@@ -311,6 +314,9 @@ pub enum SchemaType {
311314
ManifestList,
312315
ResolveResult,
313316
Resource,
317+
ResourceGetResult,
318+
ResourceSetResult,
319+
ResourceTestResult,
314320
ResourceManifest,
315321
RestartRequired,
316322
SetResult,

dsc/src/mcp/mcp_server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl McpServer {
2626
+ Self::list_dsc_functions_router()
2727
+ Self::list_dsc_resources_router()
2828
+ Self::show_dsc_resource_router()
29+
+ Self::show_dsc_schema_router()
2930
}
3031
}
3132
}

dsc/src/mcp/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod list_dsc_functions;
1515
pub mod list_dsc_resources;
1616
pub mod mcp_server;
1717
pub mod show_dsc_resource;
18+
pub mod show_dsc_schema;
1819

1920
/// This function initializes and starts the MCP server, handling any errors that may occur.
2021
///

dsc/src/mcp/show_dsc_resource.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ use dsc_lib::{
1212
};
1313
use rmcp::{ErrorData as McpError, Json, tool, tool_router, handler::server::wrapper::Parameters};
1414
use rust_i18n::t;
15-
use schemars::JsonSchema;
15+
use schemars::{JsonSchema, json_schema};
1616
use serde::{Deserialize, Serialize};
1717
use serde_json::Value;
1818
use tokio::task;
1919

20+
fn nullable_json_object_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
21+
json_schema!({"oneOf": [{"type": "null"}, {"type": "object"}]})
22+
}
23+
2024
#[derive(Serialize, JsonSchema)]
2125
pub struct DscResource {
2226
/// The namespaced name of the resource.
@@ -35,6 +39,7 @@ pub struct DscResource {
3539
#[serde(skip_serializing_if = "Option::is_none")]
3640
pub author: Option<String>,
3741
#[serde(skip_serializing_if = "Option::is_none")]
42+
#[schemars(schema_with = "nullable_json_object_schema")]
3843
pub schema: Option<Value>,
3944
}
4045

dsc/src/mcp/show_dsc_schema.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::{args::SchemaType, mcp::mcp_server::McpServer, util};
5+
use rmcp::{ErrorData as McpError, Json, tool, tool_router, handler::server::wrapper::Parameters};
6+
use schemars::{JsonSchema, json_schema};
7+
use serde::{Deserialize, Serialize};
8+
use serde_json::Value;
9+
use tokio::task;
10+
11+
fn json_object_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
12+
json_schema!({"type": "object"})
13+
}
14+
15+
#[derive(Deserialize, JsonSchema)]
16+
pub struct ShowSchemaRequest {
17+
#[schemars(description = "The schema type to retrieve the JSON schema for.")]
18+
pub r#type: SchemaType,
19+
}
20+
21+
#[derive(Serialize, JsonSchema)]
22+
pub struct ShowSchemaResponse {
23+
#[schemars(schema_with = "json_object_schema")]
24+
pub schema: Value,
25+
}
26+
27+
#[tool_router(router = show_dsc_schema_router, vis = "pub")]
28+
impl McpServer {
29+
#[tool(
30+
description = "Get the JSON schema for a specific part of using DSC, such as a configuration or output from an operation.",
31+
annotations(
32+
title = "Get the JSON schema for a specific part of using DSC",
33+
read_only_hint = true,
34+
destructive_hint = false,
35+
idempotent_hint = true,
36+
open_world_hint = true,
37+
)
38+
)]
39+
pub async fn show_dsc_schema(&self, Parameters(ShowSchemaRequest { r#type }): Parameters<ShowSchemaRequest>) -> Result<Json<ShowSchemaResponse>, McpError> {
40+
let result = task::spawn_blocking(move || {
41+
let schema = util::get_schema(r#type);
42+
Ok(ShowSchemaResponse { schema: schema.as_value().clone() })
43+
}).await.map_err(|e| McpError::internal_error(e.to_string(), None))??;
44+
45+
Ok(Json(result))
46+
}
47+
}

dsc/src/util.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use crate::args::{SchemaType, OutputFormat, TraceFormat};
55
use crate::resolve::Include;
6+
use dsc_lib::configure::config_result::{ConfigurationExportResult, ResourceGetResult, ResourceSetResult};
7+
use dsc_lib::dscresources::adapted_resource_manifest::AdaptedDscResourceManifest;
68
use dsc_lib::{
79
configure::{
810
config_doc::{
@@ -161,9 +163,15 @@ pub fn add_fields_to_json(json: &str, fields_to_add: &HashMap<String, String>) -
161163
#[must_use]
162164
pub fn get_schema(schema: SchemaType) -> Schema {
163165
match schema {
166+
SchemaType::AdaptedDscResourceManifest => {
167+
schema_for!(AdaptedDscResourceManifest)
168+
},
164169
SchemaType::Configuration => {
165170
schema_for!(Configuration)
166171
},
172+
SchemaType::ConfigurationExportResult => {
173+
schema_for!(ConfigurationExportResult)
174+
},
167175
SchemaType::ConfigurationGetResult => {
168176
schema_for!(ConfigurationGetResult)
169177
},
@@ -200,6 +208,15 @@ pub fn get_schema(schema: SchemaType) -> Schema {
200208
SchemaType::Resource => {
201209
schema_for!(Resource)
202210
},
211+
SchemaType::ResourceGetResult => {
212+
schema_for!(ResourceGetResult)
213+
},
214+
SchemaType::ResourceSetResult => {
215+
schema_for!(ResourceSetResult)
216+
},
217+
SchemaType::ResourceTestResult => {
218+
schema_for!(ResourceTestResult)
219+
},
203220
SchemaType::ResourceManifest => {
204221
schema_for!(ResourceManifest)
205222
},

dsc/tests/dsc_mcp.tests.ps1

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Describe 'Tests for MCP server' {
7676
'list_dsc_functions' = $false
7777
'list_dsc_resources' = $false
7878
'show_dsc_resource' = $false
79+
'show_dsc_schema' = $false
7980
}
8081

8182
$response = Send-McpRequest -request $mcpRequest
@@ -611,4 +612,26 @@ greeting: Hello from YAML parameters
611612
$response.error.code | Should -Be -32600
612613
$response.error.message | Should -Match 'Invalid parameters'
613614
}
615+
616+
It 'Calling show_dsc_schema works' {
617+
$mcpRequest = @{
618+
jsonrpc = "2.0"
619+
id = 19
620+
method = "tools/call"
621+
params = @{
622+
name = "show_dsc_schema"
623+
arguments = @{
624+
type = "AdaptedDscResourceManifest"
625+
}
626+
}
627+
}
628+
629+
$response = Send-McpRequest -request $mcpRequest
630+
$response.id | Should -Be 19
631+
$response.result.structuredContent | Should -Not -BeNullOrEmpty
632+
$response.result.structuredContent.schema | Should -Not -BeNullOrEmpty
633+
$schema = dsc schema --type adapted-dsc-resource-manifest | ConvertFrom-Json -Depth 20
634+
$response.result.structuredContent.schema.'$schema' | Should -Be $schema.'$schema' -Because ($response.result.structuredContent | ConvertTo-Json -Depth 20 | Out-String)
635+
$response.result.structuredContent.schema.title | Should -BeExactly $schema.title -Because ($response.result.structuredContent | ConvertTo-Json -Depth 20 | Out-String)
636+
}
614637
}

0 commit comments

Comments
 (0)