Skip to content

Commit 7509434

Browse files
author
Steve Lee (POWERSHELL HE/HIM) (from Dev Box)
committed
Add invoke_dsc_resource() tool to MCP
1 parent f272f86 commit 7509434

File tree

7 files changed

+119
-8
lines changed

7 files changed

+119
-8
lines changed

dsc/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.

dsc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ indicatif = { version = "0.18" }
2222
jsonschema = { version = "0.33", default-features = false }
2323
path-absolutize = { version = "3.1" }
2424
regex = "1.11"
25-
rmcp = { version = "0.6", features = [
25+
rmcp = { version = "0.7", features = [
2626
"server",
2727
"macros",
2828
"transport-io",

dsc/locales/en-us.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,18 @@ serverStopped = "MCP server stopped"
6666
failedToCreateRuntime = "Failed to create async runtime: %{error}"
6767
serverWaitFailed = "Failed to wait for MCP server: %{error}"
6868

69+
[mcp.invoke_dsc_resource]
70+
resourceNotFound = "Resource type '%{resource}' does not exist"
71+
6972
[mcp.list_dsc_functions]
7073
invalidNamePattern = "Invalid function name pattern '%{pattern}'"
7174

7275
[mcp.list_dsc_resources]
7376
resourceNotAdapter = "The resource '%{adapter}' is not a valid adapter"
74-
adapterNotFound = "Adapter '%{adapter}' not found"
77+
adapterNotFound = "Adapter '%{adapter}' does not exist"
7578

7679
[mcp.show_dsc_resource]
77-
resourceNotFound = "Resource type '%{type_name}' not found"
80+
resourceNotFound = "Resource type '%{type_name}' does not exist"
7881

7982
[resolve]
8083
processingInclude = "Processing Include input"

dsc/src/mcp/invoke_dsc_resource.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::mcp::mcp_server::McpServer;
5+
use dsc_lib::{
6+
configure::config_doc::ExecutionKind,
7+
dscresources::{
8+
dscresource::Invoke,
9+
invoke_result::{
10+
ExportResult,
11+
GetResult,
12+
SetResult,
13+
TestResult,
14+
},
15+
},
16+
DscManager,
17+
};
18+
use rmcp::{ErrorData as McpError, Json, tool, tool_router, handler::server::wrapper::Parameters};
19+
use rust_i18n::t;
20+
use schemars::JsonSchema;
21+
use serde::{Deserialize, Serialize};
22+
use tokio::task;
23+
24+
#[derive(Deserialize, JsonSchema)]
25+
pub enum DscOperation {
26+
Get,
27+
Set,
28+
Test,
29+
Export,
30+
}
31+
32+
#[derive(Serialize, JsonSchema)]
33+
pub enum ResourceOperationResult {
34+
GetResult(GetResult),
35+
SetResult(SetResult),
36+
TestResult(TestResult),
37+
ExportResult(ExportResult),
38+
}
39+
40+
#[derive(Deserialize, JsonSchema)]
41+
pub struct InvokeDscResourceRequest {
42+
#[schemars(description = "The operation to perform on the DSC resource")]
43+
pub operation: DscOperation,
44+
#[schemars(description = "The type name of the DSC resource to invoke")]
45+
pub resource_type: String,
46+
#[schemars(description = "The properties to pass to the DSC resource as JSON. Must match the resource JSON schema from `show_dsc_resource` tool.")]
47+
pub properties_json: String,
48+
}
49+
50+
#[tool_router(router = invoke_dsc_resource_router, vis = "pub")]
51+
impl McpServer {
52+
#[tool(
53+
description = "Invoke a DSC resource operation (Get, Set, Test, Export) with specified properties in JSON format",
54+
annotations(
55+
title = "Invoke a DSC resource operation (Get, Set, Test, Export) with specified properties in JSON format",
56+
read_only_hint = false,
57+
destructive_hint = true,
58+
idempotent_hint = true,
59+
open_world_hint = true,
60+
)
61+
)]
62+
pub async fn invoke_dsc_resource(&self, Parameters(InvokeDscResourceRequest { operation, resource_type, properties_json }): Parameters<InvokeDscResourceRequest>) -> Result<Json<ResourceOperationResult>, McpError> {
63+
let result = task::spawn_blocking(move || {
64+
let mut dsc = DscManager::new();
65+
let Some(resource) = dsc.find_resource(&resource_type, None) else {
66+
return Err(McpError::invalid_request(t!("mcp.invoke_dsc_resource.resourceNotFound", resource = resource_type), None));
67+
};
68+
match operation {
69+
DscOperation::Get => {
70+
let result = match resource.get(&properties_json) {
71+
Ok(res) => res,
72+
Err(e) => return Err(McpError::internal_error(e.to_string(), None)),
73+
};
74+
Ok(ResourceOperationResult::GetResult(result))
75+
},
76+
DscOperation::Set => {
77+
let result = match resource.set(&properties_json, false, &ExecutionKind::Actual) {
78+
Ok(res) => res,
79+
Err(e) => return Err(McpError::internal_error(e.to_string(), None)),
80+
};
81+
Ok(ResourceOperationResult::SetResult(result))
82+
},
83+
DscOperation::Test => {
84+
let result = match resource.test(&properties_json) {
85+
Ok(res) => res,
86+
Err(e) => return Err(McpError::internal_error(e.to_string(), None)),
87+
};
88+
Ok(ResourceOperationResult::TestResult(result))
89+
},
90+
DscOperation::Export => {
91+
let result = match resource.export(&properties_json) {
92+
Ok(res) => res,
93+
Err(e) => return Err(McpError::internal_error(e.to_string(), None)),
94+
};
95+
Ok(ResourceOperationResult::ExportResult(result))
96+
}
97+
}
98+
}).await.map_err(|e| McpError::internal_error(e.to_string(), None))??;
99+
100+
Ok(Json(result))
101+
}
102+
}

dsc/src/mcp/mcp_server.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ impl McpServer {
2020
#[must_use]
2121
pub fn new() -> Self {
2222
Self {
23-
tool_router: Self::list_dsc_resources_router() + Self::show_dsc_resource_router() + Self::list_dsc_functions_router(),
23+
tool_router:
24+
Self::invoke_dsc_resource_router()
25+
+ Self::list_dsc_resources_router()
26+
+ Self::show_dsc_resource_router()
27+
+ Self::list_dsc_functions_router(),
2428
}
2529
}
2630
}

dsc/src/mcp/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rmcp::{
99
};
1010
use rust_i18n::t;
1111

12+
pub mod invoke_dsc_resource;
1213
pub mod list_dsc_functions;
1314
pub mod list_dsc_resources;
1415
pub mod mcp_server;

dsc/tests/dsc_mcp.tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Describe 'Tests for MCP server' {
7171
}
7272

7373
$tools = @{
74+
'invoke_dsc_resource' = $false
7475
'list_dsc_functions' = $false
7576
'list_dsc_resources' = $false
7677
'show_dsc_resource' = $false

0 commit comments

Comments
 (0)