pub struct Manifest {Show 14 fields
pub sources: HashMap<String, String>,
pub tools: Option<ToolsConfig>,
pub agents: HashMap<String, ResourceDependency>,
pub snippets: HashMap<String, ResourceDependency>,
pub commands: HashMap<String, ResourceDependency>,
pub mcp_servers: HashMap<String, ResourceDependency>,
pub scripts: HashMap<String, ResourceDependency>,
pub hooks: HashMap<String, ResourceDependency>,
pub patches: ManifestPatches,
pub project_patches: ManifestPatches,
pub private_patches: ManifestPatches,
pub default_tools: HashMap<String, String>,
pub project: Option<ProjectConfig>,
pub manifest_dir: Option<PathBuf>,
}Fields§
§sources: HashMap<String, String>Named source repositories mapped to their Git URLs.
Keys are short, convenient names used in dependency specifications. Values are Git repository URLs (HTTPS, SSH, or local file:// URLs).
Security Note: Never include authentication tokens in these URLs. Use SSH keys or configure authentication in the global config file.
§Examples
[sources]
official = "https://github.com/claude-org/official.git"
private = "git@github.com:company/private.git"
local = "file:///home/user/local-repo"tools: Option<ToolsConfig>Tool type configurations for multi-tool support.
Maps tool type names (claude-code, opencode, agpm, custom) to their
installation configurations. This replaces the old target field and
enables support for multiple tools and custom tool types.
See ToolsConfig for details on configuration format.
agents: HashMap<String, ResourceDependency>Agent dependencies mapping names to their specifications.
Agents are typically AI model definitions, prompts, or behavioral specifications stored as Markdown files. Each dependency can be either local (filesystem path) or remote (from a Git source).
See ResourceDependency for specification format details.
snippets: HashMap<String, ResourceDependency>Snippet dependencies mapping names to their specifications.
Snippets are typically reusable code templates, examples, or documentation stored as Markdown files. They follow the same dependency format as agents.
See ResourceDependency for specification format details.
commands: HashMap<String, ResourceDependency>Command dependencies mapping names to their specifications.
Commands are Claude Code slash commands that provide custom functionality and automation within the Claude Code interface. They follow the same dependency format as agents and snippets.
See ResourceDependency for specification format details.
mcp_servers: HashMap<String, ResourceDependency>MCP server configurations mapping names to their specifications.
MCP servers provide integrations with external systems and services,
allowing Claude Code to connect to databases, APIs, and other tools.
MCP servers are JSON configuration files that get installed to
.mcp.json (no separate directory - configurations are merged into the JSON file).
See ResourceDependency for specification format details.
scripts: HashMap<String, ResourceDependency>Script dependencies mapping names to their specifications.
Scripts are executable files (.sh, .js, .py, etc.) that can be run by hooks
or independently. They are installed to .claude/scripts/ and can be
referenced by hook configurations.
See ResourceDependency for specification format details.
hooks: HashMap<String, ResourceDependency>Hook dependencies mapping names to their specifications.
Hooks are JSON configuration files that define event-based automation
in Claude Code. They specify when to run scripts based on tool usage,
prompts, and other events. Hook configurations are merged into
settings.local.json.
See ResourceDependency for specification format details.
patches: ManifestPatchesPatches for overriding resource metadata.
Patches allow overriding YAML frontmatter fields (like model) in
resources without forking upstream repositories. They are keyed by
resource type and manifest alias.
§Examples
[patch.agents.my-agent]
model = "claude-3-haiku"
temperature = "0.7"project_patches: ManifestPatchesProject-level patches (from agpm.toml).
This field is not serialized - it’s populated during loading to track which patches came from the project manifest vs private config.
private_patches: ManifestPatchesPrivate patches (from agpm.private.toml).
This field is not serialized - it’s populated during loading to track which patches came from private config. These are kept separate from project patches to maintain deterministic lockfiles.
default_tools: HashMap<String, String>Default tool overrides for resource types.
Allows users to override which tool is used by default when a dependency doesn’t explicitly specify a tool. Keys are resource type names (agents, snippets, commands, scripts, hooks, mcp-servers), values are tool names (claude-code, opencode, agpm, or custom tool names).
§Examples
[default-tools]
snippets = "claude-code" # Override default for Claude-only users
agents = "claude-code" # Explicit (already the default)
commands = "opencode" # Use OpenCode by default for commands§Built-in Defaults (when not configured)
snippets→"agpm"(shared infrastructure)- All other resource types →
"claude-code"
project: Option<ProjectConfig>Project-specific template variables.
Custom project configuration that can be referenced in resource templates via Tera template syntax. This allows teams to define project-specific values like paths, standards, and conventions that are then available throughout all installed resources.
Template access: {{ agpm.project.name }}, {{ agpm.project.paths.style_guide }}
§Examples
[project]
name = "My Project"
version = "2.0.0"
[project.paths]
style_guide = "docs/STYLE_GUIDE.md"manifest_dir: Option<PathBuf>Directory containing the manifest file (for resolving relative paths).
This field is populated when loading the manifest and is used to resolve relative paths in dependencies, particularly for path-only dependencies and their transitive dependencies.
This field is not serialized and only exists at runtime.
Implementations§
Source§impl Manifest
impl Manifest
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new empty manifest with default configuration.
The new manifest will have:
- No sources defined
- Default target directories (
.claude/agentsand.agpm/snippets) - No dependencies
This is typically used when programmatically building a manifest or as a starting point for adding dependencies.
§Examples
use agpm_cli::manifest::Manifest;
let manifest = Manifest::new();
assert!(manifest.sources.is_empty());
assert!(manifest.agents.is_empty());
assert!(manifest.snippets.is_empty());
assert!(manifest.commands.is_empty());
assert!(manifest.mcp_servers.is_empty());Sourcepub fn load(path: &Path) -> Result<Self>
pub fn load(path: &Path) -> Result<Self>
Load and parse a manifest from a TOML file.
This method reads the specified file, parses it as TOML, deserializes
it into a Manifest struct, and validates the result. The entire
operation is atomic - either the manifest loads successfully or an
error is returned.
§Validation
After parsing, the manifest is automatically validated to ensure:
- All dependency sources reference valid entries in the
[sources]section - Required fields are present and non-empty
- Version constraints are properly specified for remote dependencies
- Source URLs use supported protocols
- No version conflicts exist between dependencies
§Error Handling
Returns detailed errors for common problems:
- File I/O errors: File not found, permission denied, etc.
- TOML syntax errors: Invalid TOML format with helpful suggestions
- Validation errors: Logical inconsistencies in the manifest
- Security errors: Unsafe URL patterns or credential leakage
All errors include contextual information and actionable suggestions.
§Examples
use agpm_cli::manifest::Manifest;
use std::path::Path;
// Load a manifest file
let manifest = Manifest::load(Path::new("agpm.toml"))?;
// Access parsed data
println!("Found {} sources", manifest.sources.len());
println!("Found {} agents", manifest.agents.len());
println!("Found {} snippets", manifest.snippets.len());§File Format
Expects a valid TOML file following the AGPM manifest format. See the module-level documentation for complete format specification.
Sourcepub fn load_with_private(path: &Path) -> Result<(Self, Vec<PatchConflict>)>
pub fn load_with_private(path: &Path) -> Result<(Self, Vec<PatchConflict>)>
Load manifest with private config merged.
Loads the project manifest from agpm.toml and then attempts to load
agpm.private.toml from the same directory. If a private config exists,
its patches are merged with the project patches (private silently takes precedence).
Any conflicts (same field defined in both files with different values) are returned for informational purposes only. Private patches always override project patches without raising an error.
§Arguments
path- Path to the project manifest file (agpm.toml)
§Returns
A manifest with merged patches and a list of any conflicts detected (for informational/debugging purposes).
§Examples
use agpm_cli::manifest::Manifest;
use std::path::Path;
let (manifest, conflicts) = Manifest::load_with_private(Path::new("agpm.toml"))?;
// Conflicts are informational only - private patches already won
if !conflicts.is_empty() {
eprintln!("Note: {} private patch(es) override project settings", conflicts.len());
}Sourcepub fn get_default_tool(&self, resource_type: ResourceType) -> String
pub fn get_default_tool(&self, resource_type: ResourceType) -> String
Get the default tool for a resource type.
Checks the [default-tools] configuration first, then falls back to
the built-in defaults:
snippets→"agpm"(shared infrastructure)- All other resource types →
"claude-code"
§Arguments
resource_type- The resource type to get the default tool for
§Returns
The default tool name as a string.
§Examples
use agpm_cli::manifest::Manifest;
use agpm_cli::core::ResourceType;
let manifest = Manifest::new();
assert_eq!(manifest.get_default_tool(ResourceType::Snippet), "agpm");
assert_eq!(manifest.get_default_tool(ResourceType::Agent), "claude-code");Sourcepub fn save(&self, path: &Path) -> Result<()>
pub fn save(&self, path: &Path) -> Result<()>
Save the manifest to a TOML file with pretty formatting.
This method serializes the manifest to TOML format and writes it to the specified file path. The output is pretty-printed for human readability and follows TOML best practices.
§Formatting
The generated TOML file will:
- Use consistent indentation and spacing
- Omit empty sections for cleaner output
- Order sections logically (sources, target, agents, snippets)
- Include inline tables for detailed dependencies
§Atomic Operation
The save operation is atomic - the file is either completely written or left unchanged. This prevents corruption if the operation fails partway through.
§Error Handling
Returns detailed errors for common problems:
- Permission denied: Insufficient write permissions
- Directory doesn’t exist: Parent directory missing
- Disk full: Insufficient storage space
- File locked: Another process has the file open
§Examples
use agpm_cli::manifest::Manifest;
use std::path::Path;
let mut manifest = Manifest::new();
manifest.add_source(
"official".to_string(),
"https://github.com/claude-org/resources.git".to_string()
);
// Save to file
manifest.save(&manifest_path)?;§Output Format
The generated file will follow this structure:
[sources]
official = "https://github.com/claude-org/resources.git"
[target]
agents = ".claude/agents"
snippets = ".agpm/snippets"
[agents]
helper = { source = "official", path = "agents/helper.md", version = "v1.0.0" }
[snippets]
utils = { source = "official", path = "snippets/utils.md", version = "v1.0.0" }Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Validate the manifest structure and enforce business rules.
This method performs comprehensive validation of the manifest to ensure
logical consistency, security best practices, and correct dependency
relationships. It’s automatically called during Self::load but can
also be used independently to validate programmatically constructed manifests.
§Validation Rules
§Source Validation
- All source URLs must use supported protocols (HTTPS, SSH, git://, file://)
- No plain directory paths allowed as sources (must use file:// URLs)
- No authentication tokens embedded in URLs (security check)
- Environment variable expansion is validated for syntax
§Dependency Validation
- All dependency paths must be non-empty
- Remote dependencies must reference existing sources
- Remote dependencies must specify version constraints
- Local dependencies cannot have version constraints
- No version conflicts between dependencies with the same name
§Path Validation
- Local dependency paths are checked for proper format
- Remote dependency paths are validated as repository-relative
- Path traversal attempts are detected and rejected
§Error Types
Returns specific error types for different validation failures:
crate::core::AgpmError::SourceNotFound: Referenced source doesn’t existcrate::core::AgpmError::ManifestValidationError: General validation failures- Context errors for specific issues with actionable suggestions
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency, DetailedDependency};
let mut manifest = Manifest::new();
// This will pass validation (local dependency)
manifest.add_dependency(
"local".to_string(),
ResourceDependency::Simple("../local/helper.md".to_string()),
true
);
assert!(manifest.validate().is_ok());
// This will fail validation (missing source)
manifest.add_dependency(
"remote".to_string(),
ResourceDependency::Detailed(Box::new(DetailedDependency {
source: Some("missing".to_string()),
path: "agent.md".to_string(),
version: Some("v1.0.0".to_string()),
branch: None,
rev: None,
command: None,
args: None,
target: None,
filename: None,
dependencies: None,
tool: Some("claude-code".to_string()),
flatten: None,
install: None,
})),
true
);
assert!(manifest.validate().is_err());§Security Considerations
This method enforces critical security rules:
- Prevents credential leakage in version-controlled files
- Blocks path traversal attacks in local dependencies
- Validates URL schemes to prevent protocol confusion
- Checks for malicious patterns in dependency specifications
§Performance
Validation is designed to be fast and is safe to call frequently. Complex validations (like network connectivity) are not performed here - those are handled during dependency resolution.
Sourcepub const fn get_dependencies(
&self,
resource_type: ResourceType,
) -> Option<&HashMap<String, ResourceDependency>>
pub const fn get_dependencies( &self, resource_type: ResourceType, ) -> Option<&HashMap<String, ResourceDependency>>
Get all dependencies from both agents and snippets sections.
Returns a vector of tuples containing dependency names and their specifications. This is useful for iteration over all dependencies without needing to handle agents and snippets separately.
§Return Value
Each tuple contains:
&str: The dependency name (key from TOML)&ResourceDependency: The dependency specification
§Examples
use agpm_cli::manifest::Manifest;
let manifest = Manifest::new();
// ... add some dependencies
for (name, dep) in manifest.all_dependencies() {
println!("Dependency: {} -> {}", name, dep.get_path());
if let Some(source) = dep.get_source() {
println!(" Source: {}", source);
}
}§Order
Dependencies are returned in the order they appear in the underlying
HashMaps (agents first, then snippets, then commands), which means the order is not
guaranteed to be stable across runs.
Get dependencies for a specific resource type
Returns the HashMap of dependencies for the specified resource type.
Note: MCP servers return None as they use a different dependency type.
Sourcepub fn get_dependencies_mut(
&mut self,
resource_type: ResourceType,
) -> Option<&mut HashMap<String, ResourceDependency>>
pub fn get_dependencies_mut( &mut self, resource_type: ResourceType, ) -> Option<&mut HashMap<String, ResourceDependency>>
Get mutable dependencies for a specific resource type
Returns a mutable reference to the HashMap of dependencies for the specified resource type.
Sourcepub fn get_tools_config(&self) -> &ToolsConfig
pub fn get_tools_config(&self) -> &ToolsConfig
Get the tools configuration, returning default if not specified.
This method provides access to the tool configurations which define where resources are installed for different tools (claude-code, opencode, agpm).
Returns the configured tools or the default configuration if not specified.
Sourcepub fn get_tool_config(&self, tool: &str) -> Option<&ArtifactTypeConfig>
pub fn get_tool_config(&self, tool: &str) -> Option<&ArtifactTypeConfig>
Get configuration for a specific tool type.
Returns None if the tool is not configured.
Sourcepub fn get_artifact_resource_path(
&self,
tool: &str,
resource_type: ResourceType,
) -> Option<PathBuf>
pub fn get_artifact_resource_path( &self, tool: &str, resource_type: ResourceType, ) -> Option<PathBuf>
Get the installation path for a resource within a tool.
Returns the full installation directory path by combining:
- Tool’s base directory (e.g., “.claude”, “.opencode”)
- Resource type’s subdirectory (e.g., “agents”, “command”)
Returns None if:
- The tool is not configured
- The resource type is not supported by this tool
- The resource has no configured path (special handling like MCP merge)
Sourcepub fn get_merge_target(
&self,
tool: &str,
resource_type: ResourceType,
) -> Option<PathBuf>
pub fn get_merge_target( &self, tool: &str, resource_type: ResourceType, ) -> Option<PathBuf>
Get the merge target configuration file path for a resource type.
Returns the path to the configuration file where resources of this type should be merged (e.g., hooks, MCP servers). Returns None if the resource type doesn’t use merge targets or if the tool doesn’t support this resource type.
§Arguments
tool- The tool name (e.g., “claude-code”, “opencode”)resource_type- The resource type to look up
§Returns
The merge target path if configured, otherwise None.
§Examples
use agpm_cli::manifest::Manifest;
use agpm_cli::core::ResourceType;
let manifest = Manifest::new();
// Hooks merge into .claude/settings.local.json
let hook_target = manifest.get_merge_target("claude-code", ResourceType::Hook);
assert_eq!(hook_target, Some(".claude/settings.local.json".into()));
// MCP servers merge into .mcp.json for claude-code
let mcp_target = manifest.get_merge_target("claude-code", ResourceType::McpServer);
assert_eq!(mcp_target, Some(".mcp.json".into()));
// MCP servers merge into .opencode/opencode.json for opencode
let opencode_mcp = manifest.get_merge_target("opencode", ResourceType::McpServer);
assert_eq!(opencode_mcp, Some(".opencode/opencode.json".into()));Sourcepub fn is_resource_supported(
&self,
tool: &str,
resource_type: ResourceType,
) -> bool
pub fn is_resource_supported( &self, tool: &str, resource_type: ResourceType, ) -> bool
Check if a resource type is supported by a tool.
A resource type is considered supported if it has either:
- A configured installation path (for file-based resources)
- A configured merge target (for resources that merge into config files)
Returns true if the tool has valid configuration for the given resource type.
Sourcepub fn all_dependencies(&self) -> Vec<(&str, &ResourceDependency)>
pub fn all_dependencies(&self) -> Vec<(&str, &ResourceDependency)>
Returns all dependencies from all resource types.
This method collects dependencies from agents, snippets, commands, scripts, hooks, and MCP servers into a single vector. It’s commonly used for:
- Manifest validation across all dependency types
- Dependency resolution operations
- Generating reports of all configured dependencies
- Bulk operations on all dependencies
§Returns
A vector of tuples containing the dependency name and its configuration.
Each tuple is (name, dependency) where:
name: The dependency name as specified in the manifestdependency: Reference to theResourceDependencyconfiguration
The order follows the resource type order defined in crate::core::ResourceType::all().
§Examples
for (name, dep) in manifest.all_dependencies() {
println!("Dependency: {} -> {}", name, dep.get_path());
if let Some(source) = dep.get_source() {
println!(" Source: {}", source);
}
}Sourcepub fn all_dependencies_with_mcp(
&self,
) -> Vec<(&str, Cow<'_, ResourceDependency>)>
pub fn all_dependencies_with_mcp( &self, ) -> Vec<(&str, Cow<'_, ResourceDependency>)>
Get all dependencies including MCP servers.
All resource types now use standard ResourceDependency, so no conversion needed.
Sourcepub fn all_dependencies_with_types(
&self,
) -> Vec<(&str, Cow<'_, ResourceDependency>, ResourceType)>
pub fn all_dependencies_with_types( &self, ) -> Vec<(&str, Cow<'_, ResourceDependency>, ResourceType)>
Get all dependencies with their resource types.
Returns a vector of tuples containing the dependency name, dependency details,
and the resource type. This preserves type information that is lost in
all_dependencies_with_mcp().
This is used by the resolver to correctly type transitive dependencies without falling back to manifest section order lookups.
Dependencies for disabled tools are automatically filtered out.
Sourcepub fn has_dependency(&self, name: &str) -> bool
pub fn has_dependency(&self, name: &str) -> bool
Check if a dependency with the given name exists in any section.
Searches the [agents], [snippets], and [commands] sections for a dependency
with the specified name. This is useful for avoiding duplicate names
across different resource types.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency};
let mut manifest = Manifest::new();
manifest.add_dependency(
"helper".to_string(),
ResourceDependency::Simple("../helper.md".to_string()),
true // is_agent
);
assert!(manifest.has_dependency("helper"));
assert!(!manifest.has_dependency("nonexistent"));§Performance
This method performs two HashMap lookups, so it’s O(1) on average.
Sourcepub fn get_dependency(&self, name: &str) -> Option<&ResourceDependency>
pub fn get_dependency(&self, name: &str) -> Option<&ResourceDependency>
Get a dependency by name from any section.
Searches both the [agents] and [snippets] sections for a dependency
with the specified name, returning the first match found. Agents are
searched before snippets.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency};
let mut manifest = Manifest::new();
manifest.add_dependency(
"helper".to_string(),
ResourceDependency::Simple("../helper.md".to_string()),
true // is_agent
);
if let Some(dep) = manifest.get_dependency("helper") {
println!("Found dependency: {}", dep.get_path());
}§Search Order
Dependencies are searched in this order:
[agents]section[snippets]section[commands]section
If the same name exists in multiple sections, the first match is returned.
Sourcepub fn find_dependency(&self, name: &str) -> Option<&ResourceDependency>
pub fn find_dependency(&self, name: &str) -> Option<&ResourceDependency>
Find a dependency by name from any section (alias for get_dependency).
Searches the [agents], [snippets], and [commands] sections for a dependency
with the specified name, returning the first match found.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency};
let mut manifest = Manifest::new();
manifest.add_dependency(
"helper".to_string(),
ResourceDependency::Simple("../helper.md".to_string()),
true // is_agent
);
if let Some(dep) = manifest.find_dependency("helper") {
println!("Found dependency: {}", dep.get_path());
}Sourcepub fn add_source(&mut self, name: String, url: String)
pub fn add_source(&mut self, name: String, url: String)
Add or update a source repository in the [sources] section.
Sources map convenient names to Git repository URLs. These names can then be referenced in dependency specifications to avoid repeating long URLs throughout the manifest.
§Parameters
name: Short, convenient name for the source (e.g., “official”, “community”)url: Git repository URL (HTTPS, SSH, or file:// protocol)
§URL Validation
The URL is not validated when added - validation occurs during
Self::validate. Supported URL formats:
https://github.com/owner/repo.gitgit@github.com:owner/repo.gitfile:///absolute/path/to/repofile:///path/to/local/repo
§Examples
use agpm_cli::manifest::Manifest;
let mut manifest = Manifest::new();
// Add public repository
manifest.add_source(
"community".to_string(),
"https://github.com/claude-community/resources.git".to_string()
);
// Add private repository (SSH)
manifest.add_source(
"private".to_string(),
"git@github.com:company/private-resources.git".to_string()
);
// Add local repository
manifest.add_source(
"local".to_string(),
"file:///home/user/my-resources".to_string()
);§Security Note
Never include authentication tokens in the URL. Use SSH keys or
configure authentication globally in ~/.agpm/config.toml.
Sourcepub fn add_dependency(
&mut self,
name: String,
dep: ResourceDependency,
is_agent: bool,
)
pub fn add_dependency( &mut self, name: String, dep: ResourceDependency, is_agent: bool, )
Add or update a dependency in the appropriate section.
Adds the dependency to either the [agents], [snippets], or [commands] section
based on the is_agent parameter. If a dependency with the same name
already exists in the target section, it will be replaced.
Note: This method is deprecated in favor of Self::add_typed_dependency
which provides explicit control over resource types.
§Parameters
name: Unique name for the dependency within its sectiondep: The dependency specification (Simple or Detailed)is_agent: If true, adds to[agents]; if false, adds to[snippets](Note: UseSelf::add_typed_dependencyfor commands and other resource types)
§Validation
The dependency is not validated when added - validation occurs during
Self::validate. This allows for building manifests incrementally
before all sources are defined.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency, DetailedDependency};
let mut manifest = Manifest::new();
// Add local agent dependency
manifest.add_dependency(
"helper".to_string(),
ResourceDependency::Simple("../local/helper.md".to_string()),
true // is_agent = true
);
// Add remote snippet dependency
manifest.add_dependency(
"utils".to_string(),
ResourceDependency::Detailed(Box::new(DetailedDependency {
source: Some("community".to_string()),
path: "snippets/utils.md".to_string(),
version: Some("v1.0.0".to_string()),
branch: None,
rev: None,
command: None,
args: None,
target: None,
filename: None,
dependencies: None,
tool: Some("claude-code".to_string()),
flatten: None,
install: None,
})),
false // is_agent = false (snippet)
);§Name Conflicts
This method allows the same dependency name to exist in both the
[agents] and [snippets] sections. However, some operations like
Self::get_dependency will prefer agents over snippets when
searching by name.
Sourcepub fn add_typed_dependency(
&mut self,
name: String,
dep: ResourceDependency,
resource_type: ResourceType,
)
pub fn add_typed_dependency( &mut self, name: String, dep: ResourceDependency, resource_type: ResourceType, )
Add or update a dependency with specific resource type.
This is the preferred method for adding dependencies as it explicitly
specifies the resource type using the ResourceType enum.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency};
use agpm_cli::core::ResourceType;
let mut manifest = Manifest::new();
// Add command dependency
manifest.add_typed_dependency(
"build".to_string(),
ResourceDependency::Simple("../commands/build.md".to_string()),
ResourceType::Command
);Sourcepub fn add_mcp_server(&mut self, name: String, dependency: ResourceDependency)
pub fn add_mcp_server(&mut self, name: String, dependency: ResourceDependency)
Add or update an MCP server configuration.
MCP servers now use standard ResourceDependency format,
pointing to JSON configuration files in source repositories.
§Examples
use agpm_cli::manifest::{Manifest, ResourceDependency};
let mut manifest = Manifest::new();
// Add MCP server from source repository
manifest.add_mcp_server(
"filesystem".to_string(),
ResourceDependency::Simple("../local/mcp-servers/filesystem.json".to_string())
);