Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions agentscope-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

<!-- Jackson Java 8 Optional support -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -151,5 +157,17 @@
<artifactId>opentelemetry-api</artifactId>
<scope>test</scope>
</dependency>

<!-- Mockito for unit testing -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.agentscope.core.mcp.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.agentscope.core.mcp.message.JsonRpcError;
import io.agentscope.core.mcp.message.JsonRpcMessage;
import io.agentscope.core.mcp.message.JsonRpcNotification;
import io.agentscope.core.mcp.message.JsonRpcRequest;
import io.agentscope.core.mcp.message.JsonRpcResponse;
import io.agentscope.core.mcp.transport.TransportException;

/**
* Abstract base class for method handlers.
*
* <p>Provides common logic for handling requests and notifications.
*/
public abstract class AbstractMethodHandler implements MethodHandler {

protected ObjectMapper objectMapper = new ObjectMapper();

@Override
public JsonRpcResponse handleMessage(JsonRpcMessage message) throws TransportException {
try {
if (message instanceof JsonRpcRequest) {
JsonRpcRequest request = (JsonRpcRequest) message;
Object result = handle(request.getParams());
return new JsonRpcResponse(request.getId().orElse(null), result);
} else if (message instanceof JsonRpcNotification) {
JsonRpcNotification notification = (JsonRpcNotification) message;
handle(notification.getParams());
return null; // Notifications don't require responses
}
throw new TransportException("Unknown message type: " + message.getClass());
} catch (TransportException te) {
throw te; // Re-throw transport exceptions
} catch (Exception e) {
if (message instanceof JsonRpcRequest) {
JsonRpcRequest request = (JsonRpcRequest) message;
JsonRpcError error =
new JsonRpcError(
JsonRpcError.ErrorCode.INTERNAL_ERROR,
"Internal server error: " + e.getMessage(),
e.toString());
return new JsonRpcResponse(request.getId().orElse(null), error);
}
throw new TransportException("Error handling notification", e);
}
}
Comment thread
BEASTSHRIRAM marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.agentscope.core.mcp.handler;

import io.agentscope.core.mcp.schema.CallToolResult;
import io.agentscope.core.mcp.tool.Tool;
import io.agentscope.core.mcp.tool.ToolManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Handler for `tools/call` requests. Looks up a registered server-side Tool and executes it.
*/
public class CallToolHandler extends AbstractMethodHandler {

private final ToolManager toolManager;

public CallToolHandler(ToolManager toolManager) {
this.toolManager = toolManager;
}

@Override
public String getMethod() {
return "tools/call";
}

@SuppressWarnings("unchecked")
@Override
public Object handle(Object params) throws Exception {
if (!(params instanceof Map)) {
throw new IllegalArgumentException("Invalid params for tools/call");
}
Map<String, Object> map = (Map<String, Object>) params;
String name = (String) map.get("name");
Object arguments = map.get("arguments");

if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Tool name is required");
}

Optional<Tool> toolOpt = toolManager.get(name);
if (toolOpt.isEmpty()) {
throw new IllegalArgumentException("Tool not found: " + name);
}

Tool tool = toolOpt.get();
Object result = tool.execute(arguments);

// Normalize result into a content block list. Always wrap in a text block.
Map<String, Object> block = new HashMap<>();
block.put("type", "text");
String text;
if (result instanceof String) {
text = (String) result;
} else {
text = objectMapper.writeValueAsString(result == null ? "" : result);
}
block.put("text", text);
List<Object> content = new ArrayList<>();
content.add(block);

return new CallToolResult(content, Optional.empty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.agentscope.core.mcp.handler;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
* Registry for MCP method handlers.
*
* <p>Stores and retrieves handlers by method name.
*/
public class HandlerRegistry {

private final Map<String, MethodHandler> handlers = new ConcurrentHashMap<>();

/**
* Register a handler for a method.
*
* @param method the method name
* @param handler the handler
*/
public void register(String method, MethodHandler handler) {
handlers.put(method, handler);
}

/**
* Get a handler for a method.
*
* @param method the method name
* @return the handler, or empty if not found
*/
public Optional<MethodHandler> get(String method) {
return Optional.ofNullable(handlers.get(method));
}

/**
* Check if a handler is registered for a method.
*
* @param method the method name
* @return true if handler exists
*/
public boolean has(String method) {
return handlers.containsKey(method);
}

/**
* Unregister a handler for a method.
*
* @param method the method name
*/
public void unregister(String method) {
handlers.remove(method);
}

/**
* Get all registered method names.
*
* @return set of method names
*/
public Map<String, MethodHandler> getAll() {
return new ConcurrentHashMap<>(handlers);
}

/**
* Clear all handlers.
*/
public void clear() {
handlers.clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.agentscope.core.mcp.handler;

import io.agentscope.core.mcp.schema.InitializeResult;
import io.agentscope.core.mcp.tool.ToolManager;
import java.util.HashMap;
import java.util.Map;

/**
* Handler for `initialize` requests (handshake).
*/
public class InitializeHandler extends AbstractMethodHandler {

private final ToolManager toolManager;

public InitializeHandler(ToolManager toolManager) {
this.toolManager = toolManager;
}

@Override
public String getMethod() {
return "initialize";
}

@Override
public Object handle(Object params) throws Exception {
Map<String, Object> capabilities = new HashMap<>();
capabilities.put("tools", new HashMap<>());
capabilities.put("protocol", "mcp");

Map<String, Object> serverInfo = new HashMap<>();
serverInfo.put("name", "agentscope-core");
serverInfo.put("version", "0.1.0");

return new InitializeResult(capabilities, "2024-11-05", serverInfo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024-2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.agentscope.core.mcp.handler;

import io.agentscope.core.mcp.schema.ListToolsResult;
import io.agentscope.core.mcp.tool.Tool;
import io.agentscope.core.mcp.tool.ToolManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Handler for `tools/list` requests. Returns metadata about registered tools.
*/
public class ListToolsHandler extends AbstractMethodHandler {

private final ToolManager toolManager;

public ListToolsHandler(ToolManager toolManager) {
this.toolManager = toolManager;
}

@Override
public String getMethod() {
return "tools/list";
}

@Override
public Object handle(Object params) throws Exception {
List<Object> out = new ArrayList<>();
for (Tool t : toolManager.list()) {
Map<String, Object> m = new HashMap<>();
m.put("name", t.getName());
m.put("description", t.getDescription());
m.put("inputSchema", t.getInputSchema());
out.add(m);
}

return new ListToolsResult(Optional.empty(), out);
}
}
Loading
Loading