feat: add MCP tools for SOP discovery and retrieval#29
feat: add MCP tools for SOP discovery and retrieval#29konippi wants to merge 4 commits intostrands-agents:mainfrom
Conversation
python/strands_agents_sops/utils.py
Outdated
| return sops | ||
|
|
||
|
|
||
| def get_all_sops(sop_paths: str | None = None) -> list[dict[str, Any]]: |
There was a problem hiding this comment.
nit: Can you name this load_sops, then call each of the load_external_sops and load_builtin_sops?
| def get_all_sops(sop_paths: str | None = None) -> list[dict[str, Any]]: | |
| def load_sops(external_sop_paths: str | None = None) -> list[dict[str, Any]]: |
| return external_sops | ||
|
|
||
|
|
||
| def load_builtin_sops() -> list[dict[str, str]]: |
There was a problem hiding this comment.
Seems like there is a lot of duplicated logic in this functions and the load_external_sops, and the https://github.com/strands-agents/agent-sop/blob/main/python/strands_agents_sops/__init__.py file. Can we better consolidate this logic?
python/strands_agents_sops/utils.py
Outdated
| for sop in external_sops: | ||
| if sop["name"] not in seen_names: | ||
| seen_names.add(sop["name"]) | ||
| all_sops.append(sop) | ||
|
|
||
| # Load built-in SOPs | ||
| builtin_sops = load_builtin_sops() | ||
| for sop in builtin_sops: | ||
| if sop["name"] not in seen_names: | ||
| seen_names.add(sop["name"]) | ||
| all_sops.append(sop) |
There was a problem hiding this comment.
nit: Could you consolidate this logic into something like:
sops = load_external_sops(external_directories) + load_builtin_sops()
if sop["name"] not in seen_names:
seen_names.add(sop["name"])
all_sops.append(sop)
| logger.error(f"Error registering prompt for SOP '{sop['name']}': {e}") | ||
| continue |
There was a problem hiding this comment.
nit: Why continue and not throw here?
| return sorted(result, key=lambda x: x["name"]) | ||
|
|
||
| @self.mcp.tool() | ||
| def get_agent_sop(sop_name: str) -> dict: |
There was a problem hiding this comment.
nit: I think a simple name might be better as a parameter for an llm to generate
| def get_agent_sop(sop_name: str) -> dict: | |
| def get_agent_sop(name: str) -> dict: |
| for sop in self.sops: | ||
| if sop["name"] == sop_name: | ||
| return { | ||
| "name": sop["name"], | ||
| "description": sop["description"], | ||
| "content": sop["content"], | ||
| } |
There was a problem hiding this comment.
Should sop just be a dict instead of a list so we can do O(1) lookup?
| "description": sop["description"], | ||
| } | ||
| ) | ||
| return sorted(result, key=lambda x: x["name"]) |
There was a problem hiding this comment.
nit: Do we need to sort this? Can we remove this sorting?
| logger.error(f"Error registering prompt for SOP '{sop['name']}': {e}") | ||
| continue | ||
|
|
||
| def _create_prompt_handler(self, sop_name: str, sop_content: str): |
| self.mcp = FastMCP("agent-sop-prompt-server") | ||
| self.sops: list[dict] = [] | ||
|
|
||
| def setup(self) -> None: |
There was a problem hiding this comment.
do we need a separate setup function? Can we just do this as part of init?
python/pyproject.toml
Outdated
| "strands_agents_sops/mcp/tools.py", | ||
| "strands_agents_sops/mcp/prompts.py", |
ec1bba7 to
32b6934
Compare
Issue #, if available:
resolve #22
Description of changes:
This PR implements MCP tools that allow model-driven agents to discover and use SOPs autonomously, similar to Claude Skills. Agents can now query available SOPs and retrieve their content without requiring explicit user invocation.
How to verify
npx @modelcontextprotocol/inspector hatch run strands-agents-sops mcp --sop-paths <SOP_PATH>.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.