Skip to main content

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 validation
  • refocus(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 ClientSession
  • focus_tools: List of allowed tool names. Use ["*"] to allow all tools
  • focus_params: Dictionary mapping parameter names to allowed values
  • strict: 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 ClientSession
  • focus: Dictionary with tool-specific and global focus rules
  • strict: 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 parameters
  • focus_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 usage
  • composite_focus.py - FocusClientComposite usage
  • jwt_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

https://github.com/mimoryinc/mimory-mcp-focus

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mimory_mcp_focus-0.1.1.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mimory_mcp_focus-0.1.1-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

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

Hashes for mimory_mcp_focus-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d4163018d50e227f9694a117e800f1027ea231452487177ed7bd75fc48b2ea3c
MD5 edae0213516e472145f642274e50364c
BLAKE2b-256 eb254d24b4e97cc0bc71f24f5aa454c37b39263c58a809d1ce1e5b6b28b4d8ec

See more details on using hashes here.

File details

Details for the file mimory_mcp_focus-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for mimory_mcp_focus-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4190f6a60a07791de794878dec005e8253c75a84bbec23ed8ccabbaa94e4817e
MD5 c8d385401f87cfb685ae5e5e3fa5e0ac
BLAKE2b-256 66b4e7891c164b66c6ed1dc24cced8dbee25d956792b7d920ab0f8037bb77e78

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page