fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Error executing tool xyz: 'Context' object has no attribute 'notify' - is there a way of sending a message from server to client?

Open shrap42 opened this issue 9 months ago β€’ 4 comments

Description

I am trying to send a message (notification) from mcp server to mcp client. I use Streamable HTTP as transport. My usecase basically is to be able to interact with an application inside a webapp. There I can't run MCP server for obvious reason so I thought I would run there an MCP client connected to my MCP server. I will then have another client (Claude Desktop or in my case fast-agent implementation) from which I will instruct the LLM to do stuff, use my tools from my MCP server and also interact with the webclient. So I thought the webclient would register itself upon loading and then I want to send it some instruction, message in general, from the fast-agent client.

All chatbots recommended me something like storing the Context of the webclient when it's registering and then call context.notify({data}) when I want to send the message to it. That doesn't work, there is no method notify on the context object. So I tried using context.info(data). That sort of work, but the message was sent to the fast-agent client, the one that should act as sender, not receiver.

So my question is simple. Is there a way how to send a message from one client to another?

Example Code

# mcp_server.py
import httpx
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
# from mcp.server.fastmcp import FastMCP, Context
from fastmcp import Context, FastMCP

# Create the MCP server instance
mcp = FastMCP("Document MCP Server")

mcp_app = mcp.streamable_http_app()

# Create a FastAPI app and mount the MCP server
app = FastAPI(lifespan=mcp_app.router.lifespan_context)

# In-memory map: doc_id -> Context
doc_clients = {}

DOC_SERVICE_URL = "http://localhost:9009"

@mcp.tool()
async def create_doc(context: Context) -> str:
    async with httpx.AsyncClient() as client:
        resp = await client.post(f"{DOC_SERVICE_URL}/doc/create")
        resp.raise_for_status()
        doc_id = resp.json()["doc_id"]
    # doc_clients[doc_id] = context
    return doc_id

@mcp.tool()
async def register_viewer(doc_id: str, context: Context) -> str:
    doc_clients[doc_id] = context
    print(f"[REGISTER] Viewer registered for doc_id={doc_id}")
    return f"Viewer registered for document {doc_id}"

@mcp.tool()
async def add_line(context: Context, doc_id: str, new_line: str) -> str:
    if doc_id in doc_clients:
        print(f"Notifying client for doc_id={doc_id}: {new_line}")
        await doc_clients[doc_id].info({
            "method":"document.updated",
            "params":{"line": new_line}
        })
    return "Line added."

# Mount the MCP server’s SSE handler
# app.mount("/", mcp.sse_app())

app.mount("/", mcp_app)

Version Information

The server is started using 

uvicorn mcp_server_streamable:app --port 8008

Additional Context

No response

shrap42 avatar May 13 '25 19:05 shrap42

Related to https://github.com/jlowin/fastmcp/discussions/429#discussion-8314598

fdurant avatar May 14 '25 04:05 fdurant

Can at least someone tell me, what's the difference between these two lines? from mcp.server.fastmcp import FastMCP, Context from fastmcp import Context, FastMCP

I assume the first one is importing from the official MCP library (https://github.com/modelcontextprotocol/python-sdk) while the second one is actually from this repo, is that correct? And what is the difference in the implementation, I understood that FastMCP 1.0 was basically identical but the version 2.0+ should be only here, on this repo? Or am I completely wrong here? πŸ™‚

shrap42 avatar May 14 '25 11:05 shrap42

You're right about the imports; they come from two different libraries.

@jlowin might be able to answer your second question.

fdurant avatar May 15 '25 06:05 fdurant

And what is the difference in the implementation, I understood that FastMCP 1.0 was basically identical but the version 2.0+ should be only here, on this repo? Or am I completely wrong here? πŸ™‚

on this, I'd read this

zzstoatzz avatar May 19 '25 21:05 zzstoatzz