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
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Alongside the correctly generated pipeline yaml, an agent file is generated from
│ ├── mod.rs
│ ├── create_pr.rs
│ ├── create_work_item.rs
│ ├── edit_wiki_page.rs
│ ├── update_wiki_page.rs
│ ├── memory.rs
│ ├── missing_data.rs
│ ├── missing_tool.rs
Expand Down Expand Up @@ -894,7 +894,7 @@ safe-outputs:
- Content validation: text files are scanned for `##vso[` commands
- Extension filtering: can restrict to specific file types

#### edit-wiki-page
#### update-wiki-page
Updates the content of an existing Azure DevOps wiki page. The wiki page must already exist; this tool edits its content but does not create new pages.

**Agent parameters:**
Expand All @@ -905,7 +905,7 @@ Updates the content of an existing Azure DevOps wiki page. The wiki page must al
**Configuration options (front matter):**
```yaml
safe-outputs:
edit-wiki-page:
update-wiki-page:
wiki-name: "MyProject.wiki" # Required — wiki identifier (name or GUID)
wiki-project: "OtherProject" # Optional — ADO project that owns the wiki; defaults to current pipeline project
path-prefix: "/agent-output" # Optional — prepended to the agent-supplied path (restricts write scope)
Expand Down
2 changes: 1 addition & 1 deletion src/compile/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ pub fn generate_executor_ado_env(write_service_connection: Option<&str>) -> Stri
}

/// Safe-output names that require write access to ADO.
const WRITE_REQUIRING_SAFE_OUTPUTS: &[&str] = &["create-pull-request", "create-work-item", "create-wiki-page", "edit-wiki-page"];
const WRITE_REQUIRING_SAFE_OUTPUTS: &[&str] = &["create-pull-request", "create-work-item", "create-wiki-page", "update-wiki-page"];

/// Validate that write-requiring safe-outputs have a write service connection configured.
pub fn validate_write_permissions(front_matter: &FrontMatter) -> Result<()> {
Expand Down
20 changes: 10 additions & 10 deletions src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::path::Path;

use crate::ndjson::{self, SAFE_OUTPUT_FILENAME};
use crate::tools::{
CreatePrResult, CreateWikiPageResult, CreateWorkItemResult, EditWikiPageResult,
CreatePrResult, CreateWikiPageResult, CreateWorkItemResult, UpdateWikiPageResult,
ExecutionContext, ExecutionResult, Executor,
};

Expand Down Expand Up @@ -182,12 +182,12 @@ pub async fn execute_safe_output(
);
output.execute_sanitized(ctx).await?
}
"edit-wiki-page" => {
debug!("Parsing edit-wiki-page payload");
let mut output: EditWikiPageResult = serde_json::from_value(entry.clone())
.map_err(|e| anyhow::anyhow!("Failed to parse edit-wiki-page: {}", e))?;
"update-wiki-page" => {
debug!("Parsing update-wiki-page payload");
let mut output: UpdateWikiPageResult = serde_json::from_value(entry.clone())
.map_err(|e| anyhow::anyhow!("Failed to parse update-wiki-page: {}", e))?;
debug!(
"edit-wiki-page: path='{}', content length={}",
"update-wiki-page: path='{}', content length={}",
output.path,
output.content.len()
);
Expand Down Expand Up @@ -396,19 +396,19 @@ mod tests {
}

#[tokio::test]
async fn test_execute_malformed_edit_wiki_page_returns_err() {
async fn test_execute_malformed_update_wiki_page_returns_err() {
// Missing required fields (path and content)
let entry = serde_json::json!({"name": "edit-wiki-page"});
let entry = serde_json::json!({"name": "update-wiki-page"});
let ctx = ExecutionContext::default();

let result = execute_safe_output(&entry, &ctx).await;
assert!(result.is_err());
}

#[tokio::test]
async fn test_execute_edit_wiki_page_missing_context() {
async fn test_execute_update_wiki_page_missing_context() {
let entry = serde_json::json!({
"name": "edit-wiki-page",
"name": "update-wiki-page",
"path": "/Overview",
"content": "This is some valid wiki content."
});
Expand Down
14 changes: 7 additions & 7 deletions src/mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::sanitize::sanitize as sanitize_text;
use crate::tools::{
CreatePrParams, CreatePrResult, CreateWikiPageParams, CreateWikiPageResult,
CreateWorkItemParams, CreateWorkItemResult,
EditWikiPageParams, EditWikiPageResult, MissingDataParams, MissingDataResult,
UpdateWikiPageParams, UpdateWikiPageResult, MissingDataParams, MissingDataResult,
MissingToolParams, MissingToolResult, NoopParams, NoopResult, ToolResult,
anyhow_to_mcp_error,
};
Expand Down Expand Up @@ -406,17 +406,17 @@ impl SafeOutputs {
}

#[tool(
name = "edit-wiki-page",
name = "update-wiki-page",
description = "Create or update an Azure DevOps wiki page with the provided markdown content. \
The page path (e.g. '/Overview/Architecture') and the wiki to write to are determined by the \
pipeline configuration. Use this to publish findings, summaries, documentation, or any other \
structured output that should be visible in the project wiki."
)]
async fn edit_wiki_page(
async fn update_wiki_page(
&self,
params: Parameters<EditWikiPageParams>,
params: Parameters<UpdateWikiPageParams>,
) -> Result<CallToolResult, McpError> {
info!("Tool called: edit-wiki-page - '{}'", params.0.path);
info!("Tool called: update-wiki-page - '{}'", params.0.path);
debug!("Content length: {} chars", params.0.content.len());

// Sanitize untrusted agent-provided text fields (IS-01).
Expand All @@ -431,7 +431,7 @@ structured output that should be visible in the project wiki."
sanitized.content = sanitize_text(&sanitized.content);
sanitized.comment = sanitized.comment.map(|c| sanitize_text(&c));

let result: EditWikiPageResult = sanitized.try_into()?;
let result: UpdateWikiPageResult = sanitized.try_into()?;
let _ = self.write_safe_output_file(&result).await;

info!("Wiki page edit queued: '{}'", result.path);
Expand All @@ -445,7 +445,7 @@ structured output that should be visible in the project wiki."
name = "create-wiki-page",
description = "Create a new Azure DevOps wiki page with the provided markdown content. \
The page path (e.g. '/Overview/NewPage') and the wiki to write to are determined by the \
pipeline configuration. The page must not already exist — use edit-wiki-page to update \
pipeline configuration. The page must not already exist — use update-wiki-page to update \
existing pages. Use this to publish findings, summaries, documentation, or any other \
structured output that should be visible in the project wiki."
)]
Expand Down
8 changes: 4 additions & 4 deletions src/tools/create_wiki_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ impl Executor for CreateWikiPageResult {
if page_exists {
return Ok(ExecutionResult::failure(format!(
"Wiki page '{effective_path}' already exists. \
Use the edit-wiki-page safe output to update existing pages."
Use the update-wiki-page safe output to update existing pages."
)));
}

Expand Down Expand Up @@ -310,7 +310,7 @@ impl Executor for CreateWikiPageResult {
if put_status.as_u16() == 412 {
return Ok(ExecutionResult::failure(format!(
"Wiki page '{effective_path}' already exists (conflict detected during creation). \
Use the edit-wiki-page safe output to update existing pages."
Use the update-wiki-page safe output to update existing pages."
)));
}

Expand Down Expand Up @@ -798,7 +798,7 @@ wiki-name: "MyProject.wiki"
let result = if page_exists {
Some(ExecutionResult::failure(format!(
"Wiki page '{effective_path}' already exists. \
Use the edit-wiki-page safe output to update existing pages."
Use the update-wiki-page safe output to update existing pages."
)))
} else {
None
Expand All @@ -818,7 +818,7 @@ wiki-name: "MyProject.wiki"
let result: Option<ExecutionResult> = if page_exists {
Some(ExecutionResult::failure(format!(
"Wiki page '{effective_path}' already exists. \
Use the edit-wiki-page safe output to update existing pages."
Use the update-wiki-page safe output to update existing pages."
)))
} else {
None
Expand Down
4 changes: 2 additions & 2 deletions src/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) const PATH_SEGMENT: &AsciiSet = &CONTROLS.add(b'#').add(b'?').add(b'/
mod create_pr;
mod create_wiki_page;
mod create_work_item;
mod edit_wiki_page;
mod update_wiki_page;
pub mod memory;
mod missing_data;
mod missing_tool;
Expand All @@ -22,7 +22,7 @@ mod result;
pub use create_pr::*;
pub use create_wiki_page::*;
pub use create_work_item::*;
pub use edit_wiki_page::*;
pub use update_wiki_page::*;
pub use missing_data::*;
pub use missing_tool::*;
pub use noop::*;
Expand Down
Loading
Loading