Doubts regarding the capabalities negotiations

INFO:     incoming POST /mcp/http
2026-01-03 16:28:00,158 DEBUG pymcp.transports.mcp_http_streamable:mcp_http_streamable_post:321 [TRANSPORT][MCP_HTTP_STREAMABLE][CLIENT->SERVER] request_received session_id=<new> client_host=10.163.2.82 method=initialize id=0 phase=receive
2026-01-03 16:28:00,159 DEBUG pymcp.core.state.session:create_session:221 [DATA][SESSION][STATE] created session_id=a8b4f9ff-6072-4e52-8e47-54ae48bf18c7
2026-01-03 16:28:00,159 DEBUG pymcp.core.protocol.capabilities:negotiate_capabilities:157 [DATA][CAPABILITIES][NEGOTIATE] client_declared={'tools': True, 'prompts': True, 'resources': True, 'logging': False, 'elicitation': {}, 'roots': {'listChanged': False}} server_offered={'tools': {'listChanged': True}, 'prompts': {'listChanged': True}, 'resources': {'listChanged': True, 'subscribe': True}, 'roots': {'listChanged': True}, 'elicitation': {'form': {}, 'url': {}}, 'tasks': {'list': {}, 'cancel': {}, 'requests': {'tools': {'call': {}}}}} negotiated_for_client={'tools': {'listChanged': True}, 'prompts': {'listChanged': True}, 'resources': {'listChanged': True, 'subscribe': True}, 'roots': {'listChanged': False}, 'elicitation': {'form': {}, 'url': {}}} phase=receive
2026-01-03 16:28:00,159 DEBUG pymcp.core.protocol.payload:build_initialize:115 [DATA][CAPABILITIES][RESPOND] sending_negotiated_capabilities_to_client={'tools': {'listChanged': True}, 'prompts': {'listChanged': True}, 'resources': {'listChanged': True, 'subscribe': True}, 'roots': {'listChanged': False}, 'elicitation': {'form': {}, 'url': {}}} phase=respond
2026-01-03 16:28:00,159 DEBUG pymcp.transports.mcp_http_streamable:mcp_http_streamable_post:401 [TRANSPORT][MCP_HTTP_STREAMABLE][SERVER->CLIENT] response_json session_id=a8b4f9ff-6072-4e52-8e47-54ae48bf18c7 method=initialize id=0 status=200 phase=respond
INFO:     handled POST /mcp/http status=200 duration_ms=1.9
2026-01-03 16:28:00,160 DEBUG pymcp.core.state.session:mark_initialize_started:328 [DATA][SESSION][STATE] handshake_started state_transition=new->initialized event=initialize session_id=a8b4f9ff-6072-4e52-8e47-54ae48bf18c7 version=1

From the log(client_declared={'tools': True, 'prompts': True, 'resources': True, 'logging': False, 'elicitation': {}, 'roots': {'listChanged': False}}), you can see that we are not marking the listChanged false for tools, prompts and resources, then should the server assume that client is supporting the listChanged notification for tools, resources and prompts? According to the spec( Lifecycle - Model Context Protocol ), it’s optional, is supporting these sub-capabilities hard. If yes, what are the challenges the cursor is facing in implementing it?

If we support these sub-capabilities cursor doesn’t need to pool

Hey, thanks for the request.

Yes, you’re right. In the current version, Cursor doesn’t fully support the listChanged subcapabilities for tools/prompts/resources, even if servers declare them. This is a known limitation:

Workaround: Changing the MCP config file (mcp.json) triggers a reconnect and refreshes the tools list.

The team is aware of this limitation (there are related tickets to improve MCP support). Unfortunately, there’s no ETA yet.

If you have a specific use case that critically depends on this, please share the details. It’ll help us prioritize the feature.

I think I see what’s going on here. The client is saying they don’t support ‘listChanged’ for tools, prompts, and resources, but then the server is sending it anyway. And you’re wondering if the server should assume the client supports it.

From what I’ve read, ‘listChanged’ is optional, so it’s not too hard to implement. But if the client is explicitly saying they don’t support it, it’s probably safer to play it safe and not send it. What challenges are you running into with implementing it, if you don’t mind me asking?

no need of it cursor is polling it , I can see it in my server log.

Actually, tasks are the critical dependency. Please implement that feature if you can

exactly

Sure, different clients are sending different payloads for initialization

cursor →
```
body={“id”:0,“jsonrpc”:“2.0”,“method”:“initialize”,“params”:{“capabilities”:{“elicitation”:{},“logging”:false,“prompts”:true,“resources”:true,“roots”:{“listChanged”:false},“tools”:true},“clientInfo”:{“name”:“cursor-vscode”,“version”:“1.0.0”},“protocolVersion”:“2025-06-18”}}
```

fast-agent →
```
body={“id”:0,“jsonrpc”:“2.0”,“method”:“initialize”,“params”:{“capabilities”:{“elicitation”:{“form”:{},“url”:{}},“sampling”:{“tools”:{}}},“clientInfo”:{“name”:“fast-agent-mcp”,“version”:“0.4.17”},“protocolVersion”:“2025-11-25”}}
```

Fast-agent doesn’t share whether it supports or not, but internally supports it and I am not getting a clear answer from the spec page

1 Like

Thanks for the clarification.

Given the current behavior, I think it’s safer for the server to not assume support for listChanged notifications solely based on the negotiated capabilities. Even though the flags appear enabled during negotiation, actual runtime support on the client side seems incomplete at the moment.

Since these sub-capabilities are optional in the spec, it makes sense that Cursor prioritizes a reconnect-based refresh instead of maintaining dynamic state updates. Implementing listChanged properly likely requires more robust state tracking and synchronization logic on the client.

Until native support is available, polling or reconnecting still seems necessary, even if the negotiated capabilities suggest otherwise.

Hope it helps brother

1 Like