Replies: 1 comment
|
I have created an "epic" in #249 to track the implementation of the server features for MCP 2025-11-25 and will convert this plan into subissues |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
The latest MCP Spec is at https://modelcontextprotocol.io/specification/2025-11-25 and there is some work to be done to support its Server features (provided by the
mcp-serverlayer of the AI feature pack).I asked Claude to generate a plan to fill the gaps and implement the remaining server features.
Please find below its output for discussion. I'm planning to convert the different tasks in GitHub issues so that we can start working on it:
Context
The WildFly MCP feature pack implements an MCP server against protocol version
2025-03-26. The latest spec (2025-11-25) introduces several new fields and features. This plan identifies every gap in the server-side implementation and proposes concrete steps to close them. MCP Client features, experimental Tasks, and OAuth 2.1 are out of scope but noted for future work.Gap Summary
2025-03-26readOnlyHint,destructiveHint,idempotentHint,openWorldHint) not emitted intools/listtitlenot emitted intools/listoutputSchema+structuredContentnot supportedisErrorfield not set in tool error resultstitlenot emitted inprompts/listtitleandsizenot emitted inresources/listannotations(audience,priority,lastModified) not emittedtitlenot emitted inresources/templates/listannotationsnot emittedlistChangednot declared in capabilities for tools/prompts/resourcesnotifications/tools/list_changednot sentnotifications/prompts/list_changednot sentnotifications/resources/list_changednot sentnotifications/resources/updatednot sent (subscribe is a no-op)notifications/progress) not supportednotifications/message) not sent to clientsMCP-Protocol-Versionheader not validated on HTTP requestsImplementation Plan
Step 1: Update protocol version
File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/MCPMessageHandler.javaChange line 55:
to:
Step 2: Extend metadata records to carry new fields
File:
wildfly-mcp/injection/src/main/java/org/wildfly/extension/mcp/injection/tool/MethodMetadata.javaAdd new fields to the record:
title(String) -- human-readable display name for tools, prompts, resources, resource templatessize(int, default -1) -- resource size in bytesreadOnlyHint(Boolean) -- tool annotationdestructiveHint(Boolean) -- tool annotationidempotentHint(Boolean) -- tool annotationopenWorldHint(Boolean) -- tool annotationaudience(String) -- resource annotation (e.g. "user", "assistant")priority(double) -- resource annotationlastModified(String) -- resource annotation (ISO 8601)structuredContent(boolean) -- whether tool returns structured contentoutputSchemaType(String) -- class name for output schema generationTo avoid breaking the record constructor signature everywhere, introduce a builder or a new extended record. The cleanest approach: add the fields to
MethodMetadatawith default values by using a secondary constructor or converting to a class with a builder. SinceMethodMetadatais used in ~10 places, I recommend keeping the record but adding the new fields at the end with defaults.File:
wildfly-mcp/injection/src/main/java/org/wildfly/extension/mcp/injection/tool/MCPFeatureMetadata.javaNo changes needed -- it delegates to
MethodMetadatafor new fields.Step 3: Extract new annotation fields during deployment scanning
File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/deployment/MCPServerDependencyProcessor.java3a.
processTools()-- extract from@Toolannotation:titlefromannotation.value("title")annotationsnested annotation:readOnlyHint,destructiveHint,idempotentHint,openWorldHintstructuredContentfromannotation.value("structuredContent")outputSchema.fromclass name3b.
processPrompts()-- extract from@Promptannotation:titlefromannotation.value("title")3c.
processResources()-- extract from@Resourceannotation:titlefromannotation.value("title")sizefromannotation.value("size")annotationsnested annotation:audience,priority,lastModified3d.
processResourceTemplates()-- extract from@ResourceTemplateannotation:titlefromannotation.value("title")annotationsnested annotation:audience,priority,lastModifiedStep 4: Emit new fields in protocol responses
4a.
tools/listresponseFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ToolMessageHandler.java--toolsList()For each tool, add to the JSON response:
"title"if non-empty"annotations"object withreadOnlyHint,destructiveHint,idempotentHint,openWorldHint(only include fields that differ from spec defaults)"outputSchema"ifstructuredContentis true -- generate schema from the return type4b.
tools/callresponseFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ToolMessageHandler.java--toolsCall()structuredContentis true on the tool metadata: serialize the return value as JSON into astructuredContentfield, and also include a TextContent fallback incontent[]"isError": truein the result alongside the error content4c.
prompts/listresponseFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/PromptMessageHandler.java--promptsList()Add
"title"if non-empty.4d.
resources/listresponseFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ResourceMessageHandler.java--resourcesList()Add
"title","size"(if >= 0), and"annotations"(audience/priority/lastModified) if non-default.4e.
resources/templates/listresponseFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ResourceTemplateMessageHandler.java--resourceTemplatesList()Add
"title"and"annotations"if non-default.Step 5: Declare
listChangedin capabilitiesFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/MCPMessageHandler.javaUpdate the capabilities declaration:
This declares that the server may send list-changed notifications. The actual sending mechanism is Step 8.
Step 6: Implement per-connection log level and logging notifications
6a. Store log level on connection
File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/api/MCPConnection.javaAdd
McpLog.LogLevel logLevel()andvoid setLogLevel(McpLog.LogLevel level)methods.File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ServerSentEventResponder.javaAdd a
logLevelfield (default:McpLog.LogLevel.WARNING), implement the new methods.6b. Update
setLogLevelhandlerFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/MCPMessageHandler.java--setLogLevel()Store the level on the connection:
connection.setLogLevel(logLevel).6c. Provide
McpLogfor injection into toolsCreate an injectable
McpLogimplementation that tools can receive (similar toElicitationSender). When a tool callslog.info("message"), the server sends anotifications/messagenotification to the client via the responder, if the level is >= the connection's configured level.New file:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/McpLogImpl.javaThis wraps the connection's responder and sends:
{"jsonrpc": "2.0", "method": "notifications/message", "params": {"level": "info", "logger": "toolName", "data": "message"}}Update
ToolMessageHandler.buildArguments()to injectMcpLogparameters (same pattern asElicitationSender).Step 7: Implement progress notifications
7a. Extract
progressTokenfromtools/callrequestFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ToolMessageHandler.java--toolsCall()Read
params._meta.progressTokenfrom the message. If present, create aProgressTracker(from SDK) and make it available for injection.7b. Provide
Progressfor injection into toolsSimilar to
ElicitationSenderandMcpLog, tools can declare aProgressparameter. The implementation sendsnotifications/progressmessages to the client.Update
ToolMessageHandler.buildArguments()to injectProgressparameters.Update
MCPServerDependencyProcessor.processTools()to recognizeProgressparameter type (same asElicitationSender).Step 8: Implement list-changed and resource-updated notifications
8a. Infrastructure for sending notifications to all connections
File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/api/ConnectionManager.javaAdd a method to broadcast a notification to all active connections:
8b. Send
notifications/tools/list_changed,notifications/prompts/list_changed,notifications/resources/list_changedThese would be triggered when the registry changes (e.g., hot deployment of a new WAR). The WildFly deployment processor could fire these on deploy/undeploy. This is inherently tied to WildFly's deployment lifecycle.
For now, provide the infrastructure (broadcast method) and wire it into the deployment lifecycle hooks.
8c. Resource subscription tracking and
notifications/resources/updatedFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ResourceMessageHandler.javaCurrently
resourcesSubscribeandresourcesUnsubscribeare no-ops. Add actual subscription tracking:Map<String, Set<MCPConnection>>of URI -> subscribed connectionsresources/subscribe: add the connection to the setresources/unsubscribe: remove the connectionnotifyResourceUpdated(String uri)that sendsnotifications/resources/updatedto all subscribed connectionsThis method would be called by application code (via a CDI event or API) when a resource changes.
Step 9: Validate
MCP-Protocol-VersionheaderFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/StreamableHttpHandler.javaAfter initialization (when a session ID is already set), validate the
MCP-Protocol-Versionheader on incoming POST requests. If the header is present and doesn't match the negotiated version, return HTTP 400.Step 10: Handle AudioContent and ResourceLink in ContentMapper
File:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/api/ContentMapper.javaAudioContent(from SDK model) inprocessResultAsText()AudioContent.class-- just ensure it's recognized as aContentBlockResourceLink is not yet in the SDK model (
mcp-model-1.0.0-Beta1), so this can be deferred until the SDK adds it.Step 11: Tool error results with
isError: trueFile:
wildfly-mcp/subsystem/src/main/java/org/wildfly/extension/mcp/server/ToolMessageHandler.java--toolsCall()RunnableWhen a tool invocation throws an exception (caught in the catch blocks), instead of sending a JSON-RPC error, send a successful result with
isError: true:{"content": [{"type": "text", "text": "Error: ..."}], "isError": true}This distinguishes protocol errors (JSON-RPC error response) from tool execution errors (result with isError flag), per the spec.
Files to Modify (Summary)
injection/tool/MethodMetadata.javatitle,size, tool annotations, resource annotations,structuredContent,outputSchemaTypefieldsdeployment/MCPServerDependencyProcessor.java@Tool,@Prompt,@Resource,@ResourceTemplateserver/MCPMessageHandler.javalistChangedin capabilities, store log level on connectionserver/ToolMessageHandler.javatitle,annotations,outputSchemain tools/list; handlestructuredContent+isErrorin tools/call; injectMcpLogandProgressserver/PromptMessageHandler.javatitlein prompts/listserver/ResourceMessageHandler.javatitle,size,annotationsin resources/list; implement subscription trackingserver/ResourceTemplateMessageHandler.javatitle,annotationsin resources/templates/listserver/StreamableHttpHandler.javaMCP-Protocol-Versionheaderserver/ServerSentEventResponder.javalogLevelfield and accessorsapi/MCPConnection.javalogLevel()/setLogLevel()methodsapi/ConnectionManager.javabroadcast()methodapi/ContentMapper.javaserver/McpLogImpl.javanotifications/messageto clientsOut of Scope (Future Work)
tasks/get,tasks/result,tasks/list,tasks/cancel). Deferred for a later stage.iconsarrays on Tool, Prompt, Resource, ResourceTemplate, and serverInfo. The SDK annotation@Tool(icon=...)supports a single icon. Supporting this is straightforward but cosmetic -- defer unless needed.Last-Event-IDfor SSE reconnection. ThelastEventIdcounter exists but isn't used for replay.Verification
testsuite/mcp/to verify new fields appear intools/list,prompts/list,resources/list,resources/templates/listresponses@Tool(title="...", annotations=@Annotations(readOnlyHint=true))and verify the response includes the new fields@Tool(structuredContent=true)returning a POJO and verifystructuredContent+outputSchemain the responselogging/setLevelthen trigger a tool that logs viaMcpLog, verifynotifications/messageis received_meta.progressToken, verifynotifications/progressis sentinitializeresponse returns"protocolVersion": "2025-11-25"All reactions