Exercise the SEP-2575 stateless probes and SEP-2243 resource/prompt headers in the conformance fixtures (#3442)
This commit is contained in:
parent
979208c115
commit
10dc173637
2 changed files with 49 additions and 6 deletions
113
examples/clients/simple-chatbot/README.md
Normal file
113
examples/clients/simple-chatbot/README.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# MCP Simple Chatbot
|
||||
|
||||
This example demonstrates how to integrate the Model Context Protocol (MCP) into a simple CLI chatbot. The implementation showcases MCP's flexibility by supporting multiple tools through MCP servers and is compatible with any LLM provider that follows OpenAI API standards.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.10
|
||||
- `python-dotenv`
|
||||
- `requests`
|
||||
- `mcp`
|
||||
- `uvicorn`
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Install the dependencies:**
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Set up environment variables:**
|
||||
|
||||
Create a `.env` file in the root directory and add your API key:
|
||||
|
||||
```plaintext
|
||||
LLM_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
**Note:** The current implementation is configured to use the Groq API endpoint (`https://api.groq.com/openai/v1/chat/completions`) with the `llama-3.2-90b-vision-preview` model. If you plan to use a different LLM provider, you'll need to modify the `LLMClient` class in `main.py` to use the appropriate endpoint URL and model parameters.
|
||||
|
||||
3. **Configure servers:**
|
||||
|
||||
The `servers_config.json` follows the same structure as Claude Desktop, allowing for easy integration of multiple servers.
|
||||
Here's an example:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"sqlite": {
|
||||
"command": "uvx",
|
||||
"args": ["mcp-server-sqlite", "--db-path", "./test.db"]
|
||||
},
|
||||
"puppeteer": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Environment variables are supported as well. Pass them as you would with the Claude Desktop App.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"server_name": {
|
||||
"command": "uvx",
|
||||
"args": ["mcp-server-name", "--additional-args"],
|
||||
"env": {
|
||||
"API_KEY": "your_api_key_here"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Run the client:**
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
2. **Interact with the assistant:**
|
||||
|
||||
The assistant will automatically detect available tools and can respond to queries based on the tools provided by the configured servers.
|
||||
|
||||
3. **Exit the session:**
|
||||
|
||||
Type `quit` or `exit` to end the session.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Tool Discovery**: Tools are automatically discovered from configured servers.
|
||||
- **System Prompt**: Tools are dynamically included in the system prompt, allowing the LLM to understand available capabilities.
|
||||
- **Server Integration**: Supports any MCP-compatible server, tested with various server implementations including Uvicorn and Node.js.
|
||||
|
||||
### Class Structure
|
||||
|
||||
- **Configuration**: Manages environment variables and server configurations
|
||||
- **Server**: Handles MCP server initialization, tool discovery, and execution
|
||||
- **Tool**: Represents individual tools with their properties and formatting
|
||||
- **LLMClient**: Manages communication with the LLM provider
|
||||
- **ChatSession**: Orchestrates the interaction between user, LLM, and tools
|
||||
|
||||
### Logic Flow
|
||||
|
||||
1. **Tool Integration**:
|
||||
- Tools are dynamically discovered from MCP servers
|
||||
- Tool descriptions are automatically included in system prompt
|
||||
- Tool execution is handled through standardized MCP protocol
|
||||
|
||||
2. **Runtime Flow**:
|
||||
- User input is received
|
||||
- Input is sent to LLM with context of available tools
|
||||
- LLM response is parsed:
|
||||
- If it's a tool call → execute tool and return result
|
||||
- If it's a direct response → return to user
|
||||
- Tool results are sent back to LLM for interpretation
|
||||
- Final response is presented to user
|
||||
|
|
@ -12,7 +12,7 @@ from typing import Annotated, Any
|
|||
|
||||
import click
|
||||
from mcp.server import ServerRequestContext
|
||||
from mcp.server.mcpserver import Context, MCPServer, RequestStateSecurity
|
||||
from mcp.server.mcpserver import Context, Elicit, ElicitationResult, MCPServer, RequestStateSecurity, Resolve
|
||||
from mcp.server.mcpserver.exceptions import ToolError
|
||||
from mcp.server.mcpserver.prompts.base import Prompt, UserMessage
|
||||
from mcp.server.streamable_http import EventCallback, EventMessage, EventStore
|
||||
|
|
@ -350,6 +350,9 @@ def test_x_mcp_header(
|
|||
return f"region={region}"
|
||||
|
||||
|
||||
# SEP-2575 server-stateless diagnostics (the conformance scenario probes these tools by name)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def test_missing_capability(ctx: Context) -> str:
|
||||
"""Tests that a handler-raised MISSING_REQUIRED_CLIENT_CAPABILITY surfaces as a top-level JSON-RPC error.
|
||||
|
|
@ -370,6 +373,33 @@ async def test_missing_capability(ctx: Context) -> str:
|
|||
return "Client declared sampling capability; proceeding."
|
||||
|
||||
|
||||
def _ask_stream_probe() -> Elicit[UserResponse]:
|
||||
return Elicit("The stateless streaming probe asks for a word", UserResponse)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def test_streaming_elicitation(
|
||||
answer: Annotated[ElicitationResult[UserResponse], Resolve(_ask_stream_probe)],
|
||||
) -> str:
|
||||
"""A tool that needs elicitation, asked through a resolver (SEP-2575 / SEP-2322).
|
||||
|
||||
On 2026-07-28 the question is returned as an InputRequiredResult rather than sent
|
||||
on the response stream; on earlier versions it is a mid-call elicitation request.
|
||||
"""
|
||||
return f"elicitation {answer.action}"
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def test_logging_tool(ctx: Context) -> str:
|
||||
"""Logs once on the request-scoped channel (SEP-2575).
|
||||
|
||||
On 2026-07-28 the message is delivered only when the request's `_meta` sets
|
||||
`io.modelcontextprotocol/logLevel`.
|
||||
"""
|
||||
await ctx.info("test_logging_tool ran") # pyright: ignore[reportDeprecated]
|
||||
return "logged through the request-scoped, logLevel-gated channel"
|
||||
|
||||
|
||||
# SEP-2322 InputRequiredResult fixtures (multi-round-trip / ephemeral workflow)
|
||||
|
||||
NAME_SCHEMA = {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}
|
||||
|
|
|
|||
Loading…
Reference in a new issue