Focus-enforcing wrapper for MCP Python clients (dict-based param and tool allowlists).
Project description
mimory-mcp-focus
A focus-enforcing wrapper for MCP (Model Context Protocol) Python clients that provides fine-grained control over tool access and parameter validation through allowlists and JWT-based context extraction.
Features
- Tool Filtering: Restrict access to specific MCP tools
- Parameter Focusing: Enforce allowed values for tool parameters, only handles exact matches, ranges, and * wildcards
- JWT Integration: Extract focus context from JWT tokens
- Flexible Configuration: Support for both simple and composite focus configurations
- Range Focusing: Support for numeric range constraints
- Strict Mode: Optional strict parameter validation that requires all passed parameters be included in the focus_params
Future Work
- Full MCP Coverage: Focus all MCP features including resources, prompts, and more
- Improved Focusing: Better pattern matching for focus enforcement
- Easier JWT Integration: Signed JWTs, even when not used as access tokens, should automatically update the context
Installation
pip install mimory-mcp-focus
Quick Start
Basic Usage with Simple Focus
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mimory_mcp_focus import focus_client_simple
async def main():
# Create MCP server parameters
server_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-everything"]
)
# Connect to MCP server
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Create focused client with restrictions
focus_client = focus_client_simple(
session=session,
focus_tools=["echo", "add"], # Only allow these tools
focus_params={
"message": ["hello", "world"], # Only allow these messages
"a": ["1", "2", "3"], # Only allow these values for 'a'
"b": ["range:1-10"] # Allow any number between 1-10
},
strict=True # Require all parameters to be in focus_params
)
# List available tools (filtered)
tools = await focus_client.list_tools()
print(f"Available tools: {[tool.name for tool in tools.tools]}")
# Call allowed tool with valid parameters
result = await focus_client.call_tool("echo", {"message": "hello"})
print(f"Echo result: {result.content}")
if __name__ == "__main__":
asyncio.run(main())
Using Composite Focus
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mimory_mcp_focus import focus_client_composite
async def main():
server_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-everything"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Create composite focus client
focus_client = focus_client_composite(
session=session,
focus={
"echo": { # Specific rules for echo tool
"message": ["hello", "world", "test"]
},
"add": { # Specific rules for add tool
"a": ["range:1-100"],
"b": ["range:1-100"]
}
},
strict=False
)
# Use the focused client
result = await focus_client.call_tool("echo", {"message": "hello"})
print(f"Result: {result.content}")
if __name__ == "__main__":
asyncio.run(main())
JWT Context Extraction
from mimory_mcp_focus import extract_mcp_context_jwt, extract_mcp_composite_context_jwt
# Extract simple context from JWT
jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
jwt_key = "your-secret-key"
jwt_algorithm = "HS256"
context_params, tools_allowed = extract_mcp_context_jwt(
jwt_token=jwt_token,
jwt_key=jwt_key,
jwt_signing_algorithm=jwt_algorithm,
parameter_field="context",
tool_field="tools",
strict=True
)
print(f"Context params: {context_params}")
print(f"Tools allowed: {tools_allowed}")
# Extract composite context from JWT
composite_context = extract_mcp_composite_context_jwt(
jwt_token=jwt_token,
jwt_key=jwt_key,
jwt_signing_algorithm=jwt_algorithm,
composite_context_field="mcp_focus"
)
print(f"Composite context: {composite_context}")
Refocusing a Client
You can change the focus of an existing client without creating a new one:
from mimory_mcp_focus import refocus_client_simple, refocus_client_composite
# Refocus with simple configuration
refocus_client_simple(
session=focus_client,
focus_tools=["echo"], # Now only allow echo
focus_params={"message": ["hello"]}, # Only allow "hello" messages
strict=True
)
# Refocus with composite configuration
refocus_client_composite(
session=focus_client,
focus={
"echo": {"message": ["world", "test"]}
},
strict=False
)
API Reference
FocusedClientSession
A focus client that wraps a ClientSession and applies focus restrictions. This is the main class returned by the factory functions.
Methods
list_tools(cursor: str | None = None): List available tools (filtered by focus rules)call_tool(tool_name: str, arguments: Dict[str, Any], **kwargs): Call a tool with parameter validationrefocus(focus: Dict[str, Any], focus_type: str): Update focus configuration- All other ClientSession methods are proxied through
focus_client_simple
Factory function that focuses a ClientSession and returns a FocusedClientSession with simple focus configuration.
focus_client_simple(
session: ClientSession,
focus_tools: Optional[List[str]] = None,
focus_params: Optional[Dict[str, Union[str, List[str]]]] = None,
strict: bool = False
) -> FocusedClientSession
Parameters:
session: The underlying MCP ClientSessionfocus_tools: List of allowed tool names. Use["*"]to allow all toolsfocus_params: Dictionary mapping parameter names to allowed valuesstrict: If True, all parameters must be defined in focus_params
focus_client_composite
Factory function that focuses a ClientSession and returns a FocusedClientSession with composite focus configuration.
focus_client_composite(
session: ClientSession,
focus: Optional[Dict[str, Dict[str, List[str]]]] = None,
strict: bool = False
) -> FocusedClientSession
Parameters:
session: The underlying MCP ClientSessionfocus: Dictionary with tool-specific and global focus rulesstrict: If True, all parameters must be defined in focus rules
Focus Dictionary Structure
{
"tool_name": { # Tool-specific rules
"param_name": ["allowed_value"]
}
}
refocus_client_simple
Refocus an existing FocusedClientSession with new simple focus configuration.
refocus_client_simple(
session: FocusedClientSession,
focus_tools: List[str],
focus_params: Dict[str, Union[str, List[str]]],
strict: bool
) -> None
refocus_client_composite
Refocus an existing FocusedClientSession with new composite focus configuration.
refocus_client_composite(
session: FocusedClientSession,
focus: Dict[str, Dict[str, List[str]]],
strict: bool
) -> None
JWT Extraction Functions
extract_mcp_context_jwt
Extract simple context parameters and tools from a JWT token.
extract_mcp_context_jwt(
jwt_token: str,
jwt_key: str,
jwt_signing_algorithm: str,
parameter_field: str = 'context',
tool_field: str = 'tools',
strict: bool = False
) -> Tuple[Dict[str, Union[str, List[str]]], List[str]]
Returns:
focus_params: Dictionary of context parametersfocus_tools: List of allowed tools
extract_mcp_composite_context_jwt
Extract composite context dictionary from a JWT token.
extract_mcp_composite_context_jwt(
jwt_token: str,
jwt_key: str,
jwt_signing_algorithm: str,
composite_context_field: str
) -> Dict[str, Dict[str, List[str]]]
Returns:
focus_composite: The composite context dictionary from the JWT
Parameter Value Types
String Values
"message": ["hello", "world", "test"]
Numeric Ranges
"number": ["range:1-100"] # Any number between 1 and 100 (inclusive)
"price": ["range:0.01-999.99"] # Decimal ranges supported
Wildcard
"any_value": ["*"] # Allow any value
Comma-separated Strings
"tags": "tag1,tag2,tag3" # Automatically split into ["tag1", "tag2", "tag3"]
Error Handling
The library returns focus-related errors as CallToolResult with isError=True.
It raises ValueExceptions and ValidationExceptions depending on internal errors.
result = await focus_client.call_tool("echo", {"message": "forbidden"})
if result.isError:
print(f"Focus error: {result.content[0].text}")
For JWT extraction functions, the library raises ValueError for validation failures:
from mimory_mcp_focus import extract_mcp_context_jwt
try:
context_params, tools_allowed = extract_mcp_context_jwt(
jwt_token="invalid_token",
jwt_key="secret",
jwt_signing_algorithm="HS256"
)
except ValueError as e:
print(f"JWT validation error: {e}")
Common focus-related error scenarios:
- Tool not in allowed list
- Parameter value not allowed
- Missing required parameter in strict mode
Common exception scenarios:
- Malformed focus_params, focus_tools, or focus fields
- JWT validation failures (expired tokens, invalid signatures, etc.)
Examples
See the examples/ directory for complete working examples:
basic_usage.py- Basic FocusClient usagecomposite_focus.py- FocusClientComposite usagejwt_integration.py- JWT context extraction
Requirements
- Python >= 3.10
- mcp >= 1.14.0
- pyjwt >= 2.10.1
License
MIT License
Author
Travis McQueen - oss@mimory.io
Repository
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mimory_mcp_focus-0.1.1.tar.gz.
File metadata
- Download URL: mimory_mcp_focus-0.1.1.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4163018d50e227f9694a117e800f1027ea231452487177ed7bd75fc48b2ea3c
|
|
| MD5 |
edae0213516e472145f642274e50364c
|
|
| BLAKE2b-256 |
eb254d24b4e97cc0bc71f24f5aa454c37b39263c58a809d1ce1e5b6b28b4d8ec
|
File details
Details for the file mimory_mcp_focus-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mimory_mcp_focus-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4190f6a60a07791de794878dec005e8253c75a84bbec23ed8ccabbaa94e4817e
|
|
| MD5 |
c8d385401f87cfb685ae5e5e3fa5e0ac
|
|
| BLAKE2b-256 |
66b4e7891c164b66c6ed1dc24cced8dbee25d956792b7d920ab0f8037bb77e78
|