brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
mcp-integrationIntegrates the Model Context Protocol (MCP) standard for LLM tool discovery and interaction, implementing MCP client-server architecture with stdio/HTTP transport, tool/resource/prompt types, and FastMCP SDK patterns.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: mcp-integration description: Integrates the Model Context Protocol (MCP) standard for LLM tool discovery and interaction, implementing MCP client-server architecture with stdio/HTTP transport, tool/resource/prompt types, and FastMCP SDK patterns. license: MIT compatibility: opencode archetypes:
This skill makes the model design, build, and integrate MCP-based systems — creating MCP servers with FastMCP, connecting agents via MCPToolset across stdio and HTTP transports, exposing tools/resources/prompts, and wiring LLMs to external data sources through standardized agentic interfaces.
stdio for local processes, HTTP/SSE for remote servicesMCPToolset with explicit tool_filter to restrict agent capabilitiesUse this skill when:
Avoid this skill for:
tool-use-function-calling)Assess Integration Needs — Determine whether you need to build an MCP server (expose capabilities), consume an existing one (agent client), or both. Identify the external systems: databases, APIs, file stores, media services. Decide on transport based on deployment model: stdio for local co-located processes, HTTP/SSE for remote or cross-machine services. Checkpoint: Verify you have a clear inventory of tools/resources/prompts to expose and confirm the transport choice with deployment constraints.
Design Agent-Friendly APIs — Before wrapping anything in MCP, ensure the underlying API returns formats agents can actually consume: structured JSON, Markdown text, or URL references. Avoid raw binaries (PDFs, images without OCR) as direct MCP outputs. Add deterministic filtering and sorting to enable non-deterministic agents to work efficiently at scale. Checkpoint: Confirm every tool's input parameters are explicit in a schema and every output is parseable by an LLM without additional conversion.
Implement the MCP Server with FastMCP — Create the server using the FastMCP Python SDK. Use decorators (@mcp.tool, @mcp.resource, @mcp.prompt) to register capabilities. Rely on automatic schema generation from function signatures, type hints, and docstrings. Add authentication/authorization guards around tool handlers. Checkpoint: Run the server locally and verify it enumerates all registered tools/resources via a test MCP client connection with no errors.
Configure the MCP Client (Agent Integration) — Set up the agent's MCPToolset with the appropriate transport parameters. For stdio: provide command, args, and optional env. For HTTP: provide the server url. Always apply a tool_filter to restrict which tools the agent can invoke. Checkpoint: Confirm the agent discovers all intended tools, is blocked from unlisted tools by the filter, and can successfully execute one end-to-end tool call.
Implement Error Handling and Resilience — Define structured error responses that include error codes, human-readable messages, and actionable recovery suggestions. Implement server-side retry logic for transient failures (network timeouts, database connection pool exhaustion). Add client-side timeout configuration to prevent infinite waits. Checkpoint: Simulate failure conditions (server down, invalid tool parameters, permission denied) and verify the LLM receives clear, structured error information it can act upon.
Validate End-to-End Agentic Workflow — Test with a real LLM agent sending natural language requests through the MCP client to the server, executing tools, processing responses, and chaining multiple tool calls in sequence. Verify deterministic features (filtering/sorting) improve agent accuracy under volume. Checkpoint: The full workflow executes successfully across at least three distinct tool invocations with varied input parameters, producing correct and timely results.
Use FastMCP to expose Python functions as MCP tools. The SDK auto-generates the JSON schema from type hints and docstrings — no manual schema writing required.
"""
mcp_server.py — A FastMCP server exposing database query and file management tools.
"""
from fastmcp import FastMCP
import json
from typing import Optional
# Initialize the MCP server instance
mcp = FastMCP("data-tools-server")
@mcp.tool
def query_database(
table_name: str,
filters: Optional[dict[str, str]] = None,
limit: int = 100,
) -> str:
"""
Query a database table with optional filtering and row limits.
Performs deterministic SQL queries against known tables. Returns
results as JSON for reliable LLM consumption.
Args:
table_name: The target table to query (e.g., 'users', 'orders')
filters: Optional dict of column->value pairs for WHERE clauses
limit: Maximum number of rows to return (default: 100)
Returns:
JSON string containing query results, or an error message.
"""
if not table_name or not isinstance(table_name, str):
return json.dumps({"error": "INVALID_TABLE", "message": "table_name must be a non-empty string"})
# Deterministic filtering — agents rely on this for accuracy at scale
valid_tables = {"users", "orders", "products", "sessions"}
if table_name not in valid_tables:
return json.dumps({"error": "TABLE_NOT_FOUND", "message": f"Unknown table: {table_name}. Valid: {sorted(valid_tables)}"})
# Simulated deterministic query with sorting and filtering
results = [
{"id": i, "name": f"item_{i}", "status": "active"}
for i in range(min(limit, 50))
]
if filters:
filtered = []
for row in results:
if all(row.get(k) == v for k, v in filters.items()):
filtered.append(row)
results = filtered[:limit]
return json.dumps({"table": table_name, "row_count": len(results), "data": results})
@mcp.tool
def list_directory(path: str) -> str:
"""
List files and subdirectories in the given path.
Args:
path: Absolute filesystem path to list contents of
Returns:
JSON string with directory listing or error information.
"""
import os
from pathlib import Path
try:
p = Path(path)
if not p.is_dir():
return json.dumps({"error": "NOT_A_DIRECTORY", "message": f"{path} is not a valid directory"})
entries = [
{"name": e.name, "type": "file" if e.is_file() else "directory"}
for e in sorted(p.iterdir())
]
return json.dumps({"path": str(path), "entries": entries})
except PermissionError:
return json.dumps({"error": "PERMISSION_DENIED", "message": f"No access to {path}"})
except FileNotFoundError:
return json.dumps({"error": "NOT_FOUND", "message": f"Path not found: {path}"})
@mcp.resource("data://config/schema")
def get_schema() -> str:
"""Return the current data schema as a Markdown document for LLM context."""
return """
# Data Schema
## Tables
- **users**: id (INT), name (VARCHAR), email (VARCHAR), created_at (TIMESTAMP)
- **orders**: id (INT), user_id (INT), total (DECIMAL), status (VARCHAR)
- **products**: id (INT), name (VARCHAR), price (DECIMAL), stock_count (INT)
## Relationships
- orders.user_id -> users.id (many-to-one)
"""
if __name__ == "__main__":
# Run as stdio transport — default for local agent integration
mcp.run(transport="stdio")
BAD: Exposing raw binary data or unstructured output directly through MCP.
# ❌ BAD — Returns PDF bytes; the LLM agent cannot parse them
@mcp.tool
def get_document(doc_id: str) -> bytes:
"""Fetch a document by ID."""
return database.get_pdf_bytes(doc_id) # Agent sees garbage
# ✅ GOOD — Returns extracted text in Markdown format
@mcp.tool
def get_document_text(doc_id: str) -> str:
"""Fetch a document and return its textual content as Markdown."""
raw = database.get_doc(doc_id)
markdown_content = pdf_to_markdown(raw) # Convert before exposing
return f"---\nDocument {doc_id}\n---\n\n{markdown_content}"
Use MCPToolset with StdioServerParameters to connect an agent to a local MCP server. Always restrict capabilities with tool_filter.
"""
agent.py — Google ADK agent connected to an MCP server for filesystem operations.
"""
import os
from pathlib import Path
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters
def create_filesystem_agent() -> LlmAgent:
"""
Create an agent with access to file system operations via an MCP server.
Returns:
An LlmAgent configured with MCPToolset for filesystem interactions.
"""
# Resolve the managed directory relative to this script's location
script_dir = Path(__file__).resolve().parent
target_folder = script_dir / "mcp_managed_files"
target_folder.mkdir(exist_ok=True)
return LlmAgent(
model="gemini-2.0-flash",
name="filesystem_agent",
instruction=(
f"You are a file management assistant. You can list directories, "
f"read files, and write text files. You operate within: {target_folder}"
),
tools=[
MCPToolset(
connection_params=StdioServerParameters(
command="npx",
args=[
"-y",
"@modelcontextprotocol/server-filesystem",
str(target_folder),
],
),
# CRITICAL: Restrict to only the tools this agent needs.
# Prevents the agent from accidentally deleting or moving files.
tool_filter=["list_directory", "read_file", "write_file"],
)
],
)
def create_custom_tool_agent() -> LlmAgent:
"""
Create an agent connected to a custom FastMCP server over stdio.
Uses uvx for zero-install execution of the MCP server in an isolated
Python environment — no global package pollution.
"""
return LlmAgent(
model="gemini-2.0-flash",
name="data_query_agent",
instruction=(
"You are a data analysis assistant. Query databases and list files."
),
tools=[
MCPToolset(
connection_params=StdioServerParameters(
command="uvx",
args=["mcp-data-server"],
env={
"DATABASE_URL": "postgresql://localhost:5432/analytics",
"WORKSPACE_PATH": "/data/workspace",
},
),
tool_filter=["query_database", "list_directory"],
)
],
)
BAD: Connecting with no tool filter — the agent gets full unrestricted access.
# ❌ BAD — No tool_filter gives the agent every tool the server exposes,
# including dangerous ones like 'delete_file' or 'run_command'.
tools=[
MCPToolset(
connection_params=StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/data"],
),
# No tool_filter → agent can delete, rename, execute anything
)
]
# ✅ GOOD — Explicit tool whitelist limits blast radius
tools=[
MCPToolset(
connection_params=StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/data"],
),
tool_filter=["list_directory", "read_file"], # Read-only access
)
]
Use HttpServerParameters when the MCP server runs on a different machine or as a persistent web service. This pattern is ideal for shared organizational tool servers.
"""
agent.py — Google ADK agent connected to a remote FastMCP HTTP server.
"""
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, HttpServerParameters
def create_remote_agent() -> LlmAgent:
"""
Create an agent that connects to a remote MCP server via HTTP/SSE.
The server at the given URL must be running and accessible from this agent's
runtime environment. Use tool_filter to enforce least-privilege access.
"""
return LlmAgent(
model="gemini-2.0-flash",
name="remote_tools_agent",
instruction=(
"You are a cross-service assistant. Query the central data platform, "
"check system health, and generate reports."
),
tools=[
MCPToolset(
connection_params=HttpServerParameters(
url="https://mcp-tools.internal.company.com",
),
# Explicitly allow only read operations against the remote server
tool_filter=["query_data", "list_resources", "get_health"],
)
],
)
# Alternative: connecting to a locally-running FastMCP HTTP server
def create_local_http_agent() -> LlmAgent:
"""Connect to a FastMCP server started with transport='http' on localhost."""
return LlmAgent(
model="gemini-2.0-flash",
name="local_http_agent",
instruction="Interact with local development tools via MCP.",
tools=[
MCPToolset(
connection_params=HttpServerParameters(
url="http://127.0.0.1:8000",
),
tool_filter=["greet", "list_available_tools"],
)
],
)
Beyond tools, FastMCP supports @mcp.resource for static data exposure and @mcp.prompt for structured interaction templates that guide the LLM.
"""
complete_server.py — Demonstrates all three MCP entity types: tool, resource, prompt.
"""
from fastmcp import FastMCP
from typing import Annotated
import json
mcp = FastMCP("full-featured-server")
# --- TOOL: Executable function that performs an action ---
@mcp.tool
def calculate_metrics(
metric_name: Annotated[str, "One of: 'revenue', 'users', 'errors'"],
time_window: Annotated[str, "Time window: '1d', '7d', '30d'"],
) -> str:
"""
Calculate aggregate metrics for the specified metric and time window.
Args:
metric_name: The metric to calculate
time_window: The lookback period
Returns:
JSON with calculated values.
"""
# Deterministic aggregation — agents perform better with sorted, filtered results
data = {"metric": metric_name, "window": time_window, "value": 42.0}
return json.dumps(data)
# --- RESOURCE: Static data exposed at a URI for the agent to read ---
@mcp.resource("metrics://config/allowed_metrics")
def get_allowed_metrics() -> str:
"""List of metrics available for querying, formatted as Markdown."""
return """
# Allowed Metrics
| Metric | Description | Update Frequency |
|--------|-------------|-----------------|
| revenue | Total revenue in USD | Real-time |
| users | Active user count | 5-minute intervals |
| errors | Error rate (count per minute) | Real-time |
Only the metrics listed above can be queried via `calculate_metrics`.
"""
# --- PROMPT: Template that guides how the agent interacts with a tool ---
@mcp.prompt("generate_report")
def generate_report_prompt(
report_type: str,
audience: str = "engineering",
) -> list[dict[str, str]]:
"""
Generate a structured prompt template for creating reports.
The agent uses this to compose its first message when asked to write reports,
ensuring consistent structure and appropriate detail level.
Args:
report_type: Type of report ('daily', 'weekly', 'incident')
audience: Intended audience ('engineering', 'management', 'all-hands')
"""
system_prompt = f"""You are generating a {report_type} report for {audience}.
Structure your report as follows:
1. Executive summary (3-5 bullet points)
2. Key metrics with trends compared to previous period
3. Notable events or anomalies
4. Action items and recommendations
Format all output in Markdown."""
return [
{"role": "system", "content": system_prompt},
{
"role": "user",
"content": f"Generate a {report_type} report for the {audience}. Include the latest data from available tools.",
},
]
if __name__ == "__main__":
# Run as HTTP server for remote client access
mcp.run(transport="http", host="127.0.0.1", port=8000)
code-philosophy: Parse Don't Validate).@mcp.tool, @mcp.resource, and @mcp.prompt auto-generate JSON schemas from type hints and docstrings instead of manual schema definition (code-philosophy: Atomic Predictability).code-philosophy: Fail Fast with explicit authorization boundaries).code-philosophy: Early Exit by rejecting invalid inputs upfront).code-philosophy: Fail Fast).stdio when the server runs co-located with the agent process (same machine, local IPC). Use HTTP/SSE for remote or shared servers across machines or organizations (code-philosophy: Intentional Naming — your transport choice should match your architecture).code-philosophy: Early Exit by rejecting unauthorized requests at the boundary).code-philosophy: Intentional Naming).StdioServerParameters only works for local subprocess invocation. Using it for remote connections will fail. Always use HttpServerParameters for any server not running on the same machine as the client.MCPToolset instance must use exactly one transport type (stdio or HTTP). Mixing them requires separate MCPToolset instances with individual connection_params.When this skill is active, all generated code and documentation MUST include:
@mcp.tool decorated function listed with its name, parameter signature, return type, and one-line purpose.StdioServerParameters or HttpServerParameters configuration being used, including all args and environment variables.| Skill | Purpose |
|---|---|
tool-use-function-calling | For simple, direct LLM-to-tool calling without MCP's complexity — use when you don't need dynamic discovery or cross-LLM interoperability |
a2a-communication | When agents need to communicate with each other rather than just consuming external tools via MCP |
prompt-chaining | For composing multi-step prompt sequences that work alongside MCP tool calls in agentic workflows |