-
Notifications
You must be signed in to change notification settings - Fork 2k
Add session-specific visibility control via Context #2917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f14b0dc
Add session-specific visibility control via Context
jlowin d3afc61
Merge main into session-visibility and add session transforms
jlowin 0cf44ca
Fix import ordering in namespace activation example
jlowin 58fc710
Merge branch 'main' into session-visibility
jlowin 64bf00b
Add session visibility documentation
jlowin 1f5b638
Fix template visibility to send ResourceListChangedNotification
jlowin a9f1d2e
Add cross-reference from enabled.mdx to session visibility
jlowin 65574f9
Move session visibility docs from context.mdx to enabled.mdx
jlowin 3563c99
Add NEW tag to enabled.mdx frontmatter
jlowin e2c14a6
Normalize empty components set to None to ensure proper notifications
jlowin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Namespace Activation | ||
|
|
||
| Demonstrates session-specific visibility control using tags to organize tools into namespaces that can be activated on demand. | ||
|
|
||
| ## Pattern | ||
|
|
||
| 1. Tag tools with namespaces: `@server.tool(tags={"namespace:finance"})` | ||
| 2. Globally disable namespaces: `server.disable(tags={"namespace:finance"})` | ||
| 3. Provide activation tools that call `ctx.enable_components(tags={"namespace:finance"})` | ||
|
|
||
| Each session starts with only the activation tools visible. When a session calls an activation tool, that namespace becomes visible **only for that session**. | ||
|
|
||
| ## Run | ||
|
|
||
| ```bash | ||
| # Server | ||
| uv run python server.py | ||
|
|
||
| # Client (in another terminal) | ||
| uv run python client.py | ||
| ``` | ||
|
|
||
| ## Example Output | ||
|
|
||
| ``` | ||
| Namespace Activation Demo | ||
|
|
||
| ╭─────────────────── Initial Tools ───────────────────╮ | ||
| │ activate_finance, activate_admin, deactivate_all │ | ||
| ╰─────────────────────────────────────────────────────╯ | ||
|
|
||
| → Calling activate_finance() | ||
| Finance tools activated | ||
| ╭─────────────── After Activating Finance ────────────╮ | ||
| │ analyze_portfolio, get_market_data, execute_trade, │ | ||
| │ activate_finance, activate_admin, deactivate_all │ | ||
| ╰─────────────────────────────────────────────────────╯ | ||
|
|
||
| → Calling get_market_data(symbol='AAPL') | ||
| {'symbol': 'AAPL', 'price': 150.25, 'change': '+2.5%'} | ||
|
|
||
| → Calling activate_admin() | ||
| Admin tools activated | ||
| ╭────────────── After Activating Admin ───────────────╮ | ||
| │ analyze_portfolio, get_market_data, execute_trade, │ | ||
| │ list_users, reset_user_password, activate_finance, │ | ||
| │ activate_admin, deactivate_all │ | ||
| ╰─────────────────────────────────────────────────────╯ | ||
|
|
||
| → Calling deactivate_all() | ||
| All namespaces deactivated | ||
| ╭────────────── After Deactivating All ───────────────╮ | ||
| │ activate_finance, activate_admin, deactivate_all │ | ||
| ╰─────────────────────────────────────────────────────╯ | ||
| ``` | ||
|
Comment on lines
+25
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a language tag to the example output fence. 🔧 Suggested fix-```
+```text
Namespace Activation Demo
@@
-```
+```🧰 Tools🪛 markdownlint-cli2 (0.18.1)25-25: Fenced code blocks should have a language specified (MD040, fenced-code-language) |
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| """ | ||
| Namespace Activation Client | ||
|
|
||
| Demonstrates how session-specific visibility works from the client perspective. | ||
| """ | ||
|
|
||
| import asyncio | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| from rich import print | ||
| from rich.panel import Panel | ||
|
|
||
| from fastmcp import Client | ||
|
|
||
|
|
||
| def load_server(): | ||
| """Load the example server.""" | ||
| examples_dir = Path(__file__).parent | ||
| if str(examples_dir) not in sys.path: | ||
| sys.path.insert(0, str(examples_dir)) | ||
|
|
||
| import server as server_module | ||
|
|
||
| return server_module.server | ||
|
|
||
|
|
||
| server = load_server() | ||
|
|
||
|
|
||
| def show_tools(tools: list, title: str) -> None: | ||
| """Display available tools in a panel.""" | ||
| tool_names = [f"[cyan]{t.name}[/]" for t in tools] | ||
| print(Panel(", ".join(tool_names) or "[dim]No tools[/]", title=title)) | ||
|
|
||
|
|
||
| async def main(): | ||
| print("\n[bold]Namespace Activation Demo[/]\n") | ||
|
|
||
| async with Client(server) as client: | ||
| # Initially only activation tools are visible | ||
| tools = await client.list_tools() | ||
| show_tools(tools, "Initial Tools") | ||
|
|
||
| # Activate finance namespace | ||
| print("\n[yellow]→ Calling activate_finance()[/]") | ||
| result = await client.call_tool("activate_finance", {}) | ||
| print(f" [green]{result.data}[/]") | ||
|
|
||
| tools = await client.list_tools() | ||
| show_tools(tools, "After Activating Finance") | ||
|
|
||
| # Use a finance tool | ||
| print("\n[yellow]→ Calling get_market_data(symbol='AAPL')[/]") | ||
| result = await client.call_tool("get_market_data", {"symbol": "AAPL"}) | ||
| print(f" [green]{result.data}[/]") | ||
|
|
||
| # Activate admin namespace too | ||
| print("\n[yellow]→ Calling activate_admin()[/]") | ||
| result = await client.call_tool("activate_admin", {}) | ||
| print(f" [green]{result.data}[/]") | ||
|
|
||
| tools = await client.list_tools() | ||
| show_tools(tools, "After Activating Admin") | ||
|
|
||
| # Deactivate all - back to defaults | ||
| print("\n[yellow]→ Calling deactivate_all()[/]") | ||
| result = await client.call_tool("deactivate_all", {}) | ||
| print(f" [green]{result.data}[/]") | ||
|
|
||
| tools = await client.list_tools() | ||
| show_tools(tools, "After Deactivating All") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| asyncio.run(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| """ | ||
| Namespace Activation Server | ||
|
|
||
| Tools are organized into namespaces using tags, globally disabled by default, | ||
| and selectively enabled per-session via activation tools. | ||
| """ | ||
|
|
||
| from fastmcp import FastMCP | ||
| from fastmcp.server.context import Context | ||
|
|
||
| server = FastMCP("Multi-Domain Assistant") | ||
|
|
||
|
|
||
| # Finance namespace | ||
| @server.tool(tags={"namespace:finance"}) | ||
| def analyze_portfolio(symbols: list[str]) -> str: | ||
| """Analyze a portfolio of stock symbols.""" | ||
| return f"Portfolio analysis for: {', '.join(symbols)}" | ||
|
|
||
|
|
||
| @server.tool(tags={"namespace:finance"}) | ||
| def get_market_data(symbol: str) -> dict: | ||
| """Get current market data for a symbol.""" | ||
| return {"symbol": symbol, "price": 150.25, "change": "+2.5%"} | ||
|
|
||
|
|
||
| @server.tool(tags={"namespace:finance"}) | ||
| def execute_trade(symbol: str, quantity: int, side: str) -> str: | ||
| """Execute a trade (simulated).""" | ||
| return f"Executed {side} order: {quantity} shares of {symbol}" | ||
|
|
||
|
|
||
| # Admin namespace | ||
| @server.tool(tags={"namespace:admin"}) | ||
| def list_users() -> list[str]: | ||
| """List all system users.""" | ||
| return ["alice", "bob", "charlie"] | ||
|
|
||
|
|
||
| @server.tool(tags={"namespace:admin"}) | ||
| def reset_user_password(username: str) -> str: | ||
| """Reset a user's password (simulated).""" | ||
| return f"Password reset for {username}" | ||
|
|
||
|
|
||
| # Activation tools - always visible | ||
| @server.tool | ||
| async def activate_finance(ctx: Context) -> str: | ||
| """Activate finance tools for this session.""" | ||
| await ctx.enable_components(tags={"namespace:finance"}) | ||
| return "Finance tools activated" | ||
|
|
||
|
|
||
| @server.tool | ||
| async def activate_admin(ctx: Context) -> str: | ||
| """Activate admin tools for this session.""" | ||
| await ctx.enable_components(tags={"namespace:admin"}) | ||
| return "Admin tools activated" | ||
|
|
||
|
|
||
| @server.tool | ||
| async def deactivate_all(ctx: Context) -> str: | ||
| """Deactivate all namespaces, returning to defaults.""" | ||
| await ctx.reset_components() | ||
| return "All namespaces deactivated" | ||
|
|
||
|
|
||
| # Globally disable namespace tools by default | ||
| server.disable(tags={"namespace:finance", "namespace:admin"}) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| server.run() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.