mirror of
https://github.com/msoedov/agentic_security.git
synced 2026-08-09 11:16:03 +02:00
docs: add MCP client usage examples
This commit is contained in:
@@ -83,6 +83,25 @@ agentic_security --port=PORT --host=HOST
|
|||||||
|
|
||||||
<img width="100%" alt="booking-screen" src="https://raw.githubusercontent.com/msoedov/agentic_security/refs/heads/main/docs/images/demo.gif">
|
<img width="100%" alt="booking-screen" src="https://raw.githubusercontent.com/msoedov/agentic_security/refs/heads/main/docs/images/demo.gif">
|
||||||
|
|
||||||
|
## MCP client example
|
||||||
|
|
||||||
|
Agentic Security includes an MCP stdio server in `agentic_security.mcp.main`.
|
||||||
|
To list the available MCP tools from a local checkout:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python examples/mcp_client_usage.py
|
||||||
|
```
|
||||||
|
|
||||||
|
To call HTTP-backed tools, run the Agentic Security app first, then point the
|
||||||
|
MCP server at it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
agentic_security --host 127.0.0.1 --port 8718
|
||||||
|
python examples/mcp_client_usage.py --agentic-security-url http://127.0.0.1:8718 --call get_spec_templates
|
||||||
|
```
|
||||||
|
|
||||||
|
See `docs/mcp_client_usage.md` for the full walkthrough.
|
||||||
|
|
||||||
## LLM kwargs
|
## LLM kwargs
|
||||||
|
|
||||||
Agentic Security uses plain text HTTP spec like:
|
Agentic Security uses plain text HTTP spec like:
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import sys
|
||||||
|
|
||||||
from mcp import ClientSession, StdioServerParameters
|
from mcp import ClientSession, StdioServerParameters
|
||||||
from mcp.client.stdio import stdio_client
|
from mcp.client.stdio import stdio_client
|
||||||
|
|
||||||
from agentic_security.logutils import logger
|
from agentic_security.logutils import logger
|
||||||
|
|
||||||
# Create server parameters for stdio connection
|
|
||||||
server_params = StdioServerParameters(
|
def build_server_params() -> StdioServerParameters:
|
||||||
command="python", # Executable
|
"""Create server parameters for a stdio MCP client session."""
|
||||||
args=["agentic_security/mcp/main.py"], # Your server script
|
return StdioServerParameters(
|
||||||
env=None, # Optional environment variables
|
command=sys.executable,
|
||||||
)
|
args=["-m", "agentic_security.mcp.main"],
|
||||||
|
env=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def run() -> None:
|
async def run() -> None:
|
||||||
try:
|
try:
|
||||||
|
server_params = build_server_params()
|
||||||
logger.info(
|
logger.info(
|
||||||
"Starting stdio client session with server parameters: %s", server_params
|
"Starting stdio client session with server parameters: %s", server_params
|
||||||
)
|
)
|
||||||
async with stdio_client(server_params) as (read, write):
|
async with stdio_client(server_params) as (read, write):
|
||||||
async with ClientSession(read, write) as session:
|
async with ClientSession(read, write) as session:
|
||||||
# Initialize the connection --> connection does not work
|
|
||||||
logger.info("Initializing client session...")
|
logger.info("Initializing client session...")
|
||||||
await session.initialize()
|
await session.initialize()
|
||||||
|
|
||||||
# List available prompts, resources, and tools --> no avalialbe tools
|
|
||||||
logger.info("Listing available prompts...")
|
logger.info("Listing available prompts...")
|
||||||
prompts = await session.list_prompts()
|
prompts = await session.list_prompts()
|
||||||
logger.info(f"Available prompts: {prompts}")
|
logger.info(f"Available prompts: {prompts}")
|
||||||
@@ -36,26 +38,10 @@ async def run() -> None:
|
|||||||
logger.info("Listing available tools...")
|
logger.info("Listing available tools...")
|
||||||
tools = await session.list_tools()
|
tools = await session.list_tools()
|
||||||
logger.info(f"Available tools: {tools}")
|
logger.info(f"Available tools: {tools}")
|
||||||
|
logger.info(
|
||||||
# Call the echo tool --> echo tool issue
|
"Available MCP tool names: %s",
|
||||||
logger.info("Calling echo_tool with message...")
|
", ".join(tool.name for tool in tools.tools),
|
||||||
echo_result = await session.call_tool(
|
|
||||||
"echo_tool", arguments={"message": "Hello from client!"}
|
|
||||||
)
|
)
|
||||||
logger.info(f"Tool result: {echo_result}")
|
|
||||||
|
|
||||||
# # Read the echo resource
|
|
||||||
# echo_content, mime_type = await session.read_resource(
|
|
||||||
# "echo://Hello_resource"
|
|
||||||
# )
|
|
||||||
# logger.info(f"Resource content: {echo_content}")
|
|
||||||
# logger.info(f"Resource MIME type: {mime_type}")
|
|
||||||
|
|
||||||
# # Get and use the echo prompt
|
|
||||||
# prompt_result = await session.get_prompt(
|
|
||||||
# "echo_prompt", arguments={"message": "Hello prompt!"}
|
|
||||||
# )
|
|
||||||
# logger.info(f"Prompt result: {prompt_result}")
|
|
||||||
|
|
||||||
logger.info("Client operations completed successfully.")
|
logger.info("Client operations completed successfully.")
|
||||||
return prompts, resources, tools
|
return prompts, resources, tools
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
@@ -8,7 +10,7 @@ mcp = FastMCP(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# FastAPI Server Configuration
|
# FastAPI Server Configuration
|
||||||
AGENTIC_SECURITY = "http://0.0.0.0:8718"
|
AGENTIC_SECURITY = os.getenv("AGENTIC_SECURITY_URL", "http://0.0.0.0:8718")
|
||||||
|
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# MCP client usage
|
||||||
|
|
||||||
|
Agentic Security exposes an MCP stdio server in `agentic_security.mcp.main`.
|
||||||
|
The example client in `examples/mcp_client_usage.py` shows how to connect to
|
||||||
|
that server, list available tools, and optionally call simple no-argument tools.
|
||||||
|
|
||||||
|
## List MCP tools
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python examples/mcp_client_usage.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts the MCP server as a subprocess with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python -m agentic_security.mcp.main
|
||||||
|
```
|
||||||
|
|
||||||
|
The client initializes an MCP session and prints the available Agentic Security
|
||||||
|
tools, including `verify_llm`, `start_scan`, `stop_scan`, `get_data_config`, and
|
||||||
|
`get_spec_templates`.
|
||||||
|
|
||||||
|
## Call an HTTP-backed tool
|
||||||
|
|
||||||
|
Some MCP tools call the Agentic Security HTTP app. Start the app in another
|
||||||
|
terminal first:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
agentic_security --host 127.0.0.1 --port 8718
|
||||||
|
```
|
||||||
|
|
||||||
|
Then point the MCP server at that app and call a no-argument tool:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python examples/mcp_client_usage.py \
|
||||||
|
--agentic-security-url http://127.0.0.1:8718 \
|
||||||
|
--call get_spec_templates
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set `AGENTIC_SECURITY_URL` directly:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
AGENTIC_SECURITY_URL=http://127.0.0.1:8718 python examples/mcp_client_usage.py --call get_data_config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use the package helper
|
||||||
|
|
||||||
|
For tests or quick local checks, `agentic_security.mcp.client.run()` creates the
|
||||||
|
same stdio session and returns the prompt, resource, and tool list results:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from agentic_security.mcp.client import run
|
||||||
|
|
||||||
|
|
||||||
|
async def main() -> None:
|
||||||
|
_prompts, _resources, tools = await run()
|
||||||
|
print([tool.name for tool in tools.tools])
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Example MCP client for the Agentic Security stdio server.
|
||||||
|
|
||||||
|
The default command lists the tools exposed by ``agentic_security.mcp.main``.
|
||||||
|
If the Agentic Security HTTP app is running, pass ``--call`` to invoke one of
|
||||||
|
the no-argument HTTP-backed tools through MCP.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from mcp import ClientSession, StdioServerParameters
|
||||||
|
from mcp.client.stdio import stdio_client
|
||||||
|
|
||||||
|
|
||||||
|
NO_ARGUMENT_TOOLS = {"get_data_config", "get_spec_templates", "stop_scan"}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_server_params(agentic_security_url: str | None) -> StdioServerParameters:
|
||||||
|
env = os.environ.copy()
|
||||||
|
if agentic_security_url:
|
||||||
|
env["AGENTIC_SECURITY_URL"] = agentic_security_url
|
||||||
|
|
||||||
|
return StdioServerParameters(
|
||||||
|
command=sys.executable,
|
||||||
|
args=["-m", "agentic_security.mcp.main"],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _jsonable(value: Any) -> Any:
|
||||||
|
if hasattr(value, "model_dump"):
|
||||||
|
return value.model_dump(mode="json")
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
return [_jsonable(item) for item in value]
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {key: _jsonable(item) for key, item in value.items()}
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
async def run_client(agentic_security_url: str | None, call_tool: str | None) -> None:
|
||||||
|
server_params = _build_server_params(agentic_security_url)
|
||||||
|
|
||||||
|
async with stdio_client(server_params) as (read, write):
|
||||||
|
async with ClientSession(read, write) as session:
|
||||||
|
await session.initialize()
|
||||||
|
tools = await session.list_tools()
|
||||||
|
tool_names = [tool.name for tool in tools.tools]
|
||||||
|
|
||||||
|
print("Available Agentic Security MCP tools:")
|
||||||
|
for tool in tools.tools:
|
||||||
|
description_lines = (tool.description or "").strip().splitlines()
|
||||||
|
description = description_lines[0] if description_lines else "No description"
|
||||||
|
print(f"- {tool.name}: {description}")
|
||||||
|
|
||||||
|
if not call_tool:
|
||||||
|
return
|
||||||
|
|
||||||
|
if call_tool not in tool_names:
|
||||||
|
raise ValueError(
|
||||||
|
f"Unknown tool {call_tool!r}. Available tools: {', '.join(tool_names)}"
|
||||||
|
)
|
||||||
|
if call_tool not in NO_ARGUMENT_TOOLS:
|
||||||
|
raise ValueError(
|
||||||
|
f"{call_tool!r} requires arguments. This example only calls "
|
||||||
|
f"no-argument tools: {', '.join(sorted(NO_ARGUMENT_TOOLS))}"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await session.call_tool(call_tool, arguments={})
|
||||||
|
print()
|
||||||
|
print(f"{call_tool} result:")
|
||||||
|
print(json.dumps(_jsonable(result), indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="List Agentic Security MCP tools and optionally call one.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--agentic-security-url",
|
||||||
|
default=None,
|
||||||
|
help=(
|
||||||
|
"Agentic Security HTTP app URL. Defaults to AGENTIC_SECURITY_URL "
|
||||||
|
"or http://0.0.0.0:8718 in the server."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--call",
|
||||||
|
choices=sorted(NO_ARGUMENT_TOOLS),
|
||||||
|
help="Optional no-argument MCP tool to call after listing tools.",
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parse_args()
|
||||||
|
asyncio.run(run_client(args.agentic_security_url, args.call))
|
||||||
@@ -26,6 +26,7 @@ nav:
|
|||||||
- Dataset Extension: datasets.md
|
- Dataset Extension: datasets.md
|
||||||
- External Modules: external_module.md
|
- External Modules: external_module.md
|
||||||
- CI/CD Integration: ci_cd.md
|
- CI/CD Integration: ci_cd.md
|
||||||
|
- MCP Client Usage: mcp_client_usage.md
|
||||||
- Bayesian Optimization: optimizer.md
|
- Bayesian Optimization: optimizer.md
|
||||||
- Image Generation: image_generation.md
|
- Image Generation: image_generation.md
|
||||||
- Stenography Functions: stenography.md
|
- Stenography Functions: stenography.md
|
||||||
|
|||||||
+13
-5
@@ -4,9 +4,17 @@ from agentic_security.mcp.client import run
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mcp_echo_tool():
|
async def test_mcp_client_lists_agentic_security_tools():
|
||||||
"""Test the echo tool functionality"""
|
"""Test that the MCP client can discover the server tools."""
|
||||||
prompts, resources, tools = await run()
|
prompts, resources, tools = await run()
|
||||||
assert prompts
|
tool_names = {tool.name for tool in tools.tools}
|
||||||
assert resources
|
|
||||||
assert tools
|
assert prompts is not None
|
||||||
|
assert resources is not None
|
||||||
|
assert {
|
||||||
|
"verify_llm",
|
||||||
|
"start_scan",
|
||||||
|
"stop_scan",
|
||||||
|
"get_data_config",
|
||||||
|
"get_spec_templates",
|
||||||
|
}.issubset(tool_names)
|
||||||
|
|||||||
Reference in New Issue
Block a user