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
Metadata
Release files for mimory-mcp-focus 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mimory_mcp_focus-0.1.1.tar.gz | 13.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mimory_mcp_focus-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.5 kB
Release files / mimory_mcp_focus-0.1.1.tar.gz
| Download URL | mimory_mcp_focus-0.1.1.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d4163018d50e227f9694a117e800f1027ea231452487177ed7bd75fc48b2ea3c
|
|
BLAKE2b-256 checksum How to use checksums |
eb254d24b4e97cc0bc71f24f5aa454c37b39263c58a809d1ce1e5b6b28b4d8ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / mimory_mcp_focus-0.1.1-py3-none-any.whl
| Download URL | mimory_mcp_focus-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4190f6a60a07791de794878dec005e8253c75a84bbec23ed8ccabbaa94e4817e
|
|
BLAKE2b-256 checksum How to use checksums |
66b4e7891c164b66c6ed1dc24cced8dbee25d956792b7d920ab0f8037bb77e78
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Security for mimory-mcp-focus 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.