Skip to content

Commit

Permalink
format pty4j-mcp-util output
Browse files Browse the repository at this point in the history
  • Loading branch information
TreasureJade committed Feb 25, 2025
2 parents 5ae0eff + c112e97 commit b00204a
Show file tree
Hide file tree
Showing 10 changed files with 422 additions and 0 deletions.
17 changes: 17 additions & 0 deletions jcommon/mcp/mcp-ipinfo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
```json
# 配置MCP
{
"mcpServers": {
"ipinfo": {
"command": "/usr/bin/java",
"args": [
"-jar",
"/Users/kevin/Coding/mone/jcommon/mcp/mcp-ipinfo/target/app.jar"
],
"env": {
"IPINFO_API_TOKEN": "<YOUR TOKEN>"
}
}
}
}
```
61 changes: 61 additions & 0 deletions jcommon/mcp/mcp-ipinfo/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>run.mone</groupId>
<artifactId>mcp</artifactId>
<version>1.6.1-jdk21-SNAPSHOT</version>
</parent>

<artifactId>mcp-ipinfo</artifactId>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>io.ipinfo</groupId>
<artifactId>ipinfo-api</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.14</version>
<configuration>
<mainClass>run.mone.mcp.ipinfo.IpinfoMcpBootstrap</mainClass>
<finalName>app</finalName>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.mone.mcp.ipinfo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("run.mone.mcp.ipinfo")
@Slf4j
public class IpinfoMcpBootstrap {
public static void main(String[] args) {
log.info("mcp ipinfo start");
SpringApplication.run(IpinfoMcpBootstrap.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package run.mone.mcp.ipinfo.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.ObjectMapper;

import run.mone.hive.mcp.server.transport.StdioServerTransport;
import run.mone.mcp.ipinfo.function.IpinfoFunction;

@Configuration
public class IpinfoConfig {

@Bean
public IpinfoFunction ipinfoFunction(ObjectMapper objectMapper) {
return new IpinfoFunction(objectMapper);
}

@Bean
StdioServerTransport stdioServerTransport(ObjectMapper mapper) {
return new StdioServerTransport(mapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package run.mone.mcp.ipinfo.function;

import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import run.mone.hive.mcp.spec.McpSchema;
import run.mone.mcp.ipinfo.service.IpinfoService;

@Data
@Slf4j
public class IpinfoFunction implements Function<Map<String, Object>, McpSchema.CallToolResult> {

private String name = "ipinfo_executor";
private String desc = "ipinfo executor";
private ObjectMapper objectMapper;

private String ipinfoToolSchema = """
{
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["get_ip", "get_ip_details"],
"description": "Get IP address and information about an IP address.\
Use this tool to:\
- Determine the user's geographic location to coarse granularity\
- Get information about the user's internet service provider\
- Get IP address and information about a specific IP address"
},
"ip": {
"type": "string",
"description": "The IP address to look up. If None, returns information about the requesting client's IP address."
}
},
"required": ["operation"]
}
""";

public IpinfoFunction(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public McpSchema.CallToolResult apply(Map<String, Object> params) {
String IPINFO_API_TOKEN = System.getenv().getOrDefault("IPINFO_API_TOKEN", "");

String operation = (String) params.get("operation");
String ip = (String) params.get("ip");

if (operation == null || operation.trim().isEmpty()) {
throw new IllegalArgumentException("Operation is required");
}

if (operation.equals("get_ip")) {
return getIp();
} else if (operation.equals("get_ip_details")) {
if (ip == null || ip.trim().isEmpty()) {
ip = IpinfoService.getClientIp();
}
return getIpDetail(IPINFO_API_TOKEN, ip);
} else {
throw new IllegalArgumentException("Invalid operation");
}
}

public McpSchema.CallToolResult getIp() {
String clientIp = IpinfoService.getClientIp();

if(!clientIp.isEmpty()) {
return new McpSchema.CallToolResult(
List.of(new McpSchema.TextContent("Client IP: " + clientIp)),
false
);
} else {
return new McpSchema.CallToolResult(
List.of(new McpSchema.TextContent("Get IP failed")),
true
);
}
}

public McpSchema.CallToolResult getIpDetail(String IPINFO_API_TOKEN, String ip) {
try {
Map<String, Object> ipDetail = IpinfoService.getIpDetail(IPINFO_API_TOKEN, ip);

if (ipDetail == null || ipDetail.isEmpty()) {
log.error("Get IP detail failed");
return new McpSchema.CallToolResult(
List.of(new McpSchema.TextContent("Get IP detail failed")),
true
);
}
log.info("Get IP detail success");
return new McpSchema.CallToolResult(
List.of(new McpSchema.TextContent(new Gson().toJson(ipDetail))),
false
);
} catch (Exception e) {
log.error("Get IP detail failed: ", e);
return new McpSchema.CallToolResult(
List.of(new McpSchema.TextContent("Error: " + e.getMessage())),
true
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package run.mone.mcp.ipinfo.server;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import run.mone.hive.mcp.server.McpServer;
import run.mone.hive.mcp.server.McpServer.ToolRegistration;
import run.mone.hive.mcp.server.McpSyncServer;
import run.mone.hive.mcp.spec.McpSchema.ServerCapabilities;
import run.mone.hive.mcp.spec.McpSchema.Tool;
import run.mone.hive.mcp.spec.ServerMcpTransport;
import run.mone.mcp.ipinfo.function.IpinfoFunction;

@Slf4j
@Component
public class IpinfoMcpServer {

private final ServerMcpTransport transport;

private McpSyncServer syncServer;

public IpinfoMcpServer(ServerMcpTransport transport) {
this.transport = transport;
log.info("IpinfoMcpServer initialized with transport: {}", transport);
}

public McpSyncServer start() {
log.info("Starting IpinfoMcpServer...");
McpSyncServer syncServer = McpServer.using(transport)
.serverInfo("ipinfo_mcp_server", "1.0.0")
.capabilities(ServerCapabilities.builder()
.tools(true)
.logging()
.build())
.sync();

log.info("Registering ipinfo tool...");

try {
IpinfoFunction ipinfoFunction = new IpinfoFunction(new ObjectMapper());
var ipinfoToolRegistration = new ToolRegistration(
new Tool(ipinfoFunction.getName(), ipinfoFunction.getDesc(), ipinfoFunction.getIpinfoToolSchema()),
ipinfoFunction
);
syncServer.addTool(ipinfoToolRegistration);
log.info("Successfully registered ipinfo tool");
} catch (Exception e) {
log.error("Failed to register ipinfo tool", e);
throw e;
}

return syncServer;
}

@PostConstruct
public void init() {
this.syncServer = start();
}

@PreDestroy
public void stop() {
if (this.syncServer != null) {
log.info("Stopping IpinfoMcpServer...");
this.syncServer.closeGracefully();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package run.mone.mcp.ipinfo.service;

import java.util.Map;
import java.util.HashMap;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;

import com.google.gson.Gson;

import io.ipinfo.api.IPinfo;
import io.ipinfo.api.errors.RateLimitedException;
import io.ipinfo.api.model.IPResponse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;

@Slf4j
public class IpinfoService {
public static Map<String, Object> getIpDetail(String token, String ip) {
IPinfo ipInfo = new IPinfo.Builder()
.setToken(token)
.build();

try {
IPResponse response = ipInfo.lookupIP(ip);

Map<String, Object> result = new HashMap<>();
result.put("city", response.getCity());
result.put("region", response.getRegion());
result.put("country", response.getCountryName());
result.put("latitude", response.getLatitude());
result.put("longitude", response.getLongitude());
result.put("timezone", response.getTimezone());

return result;
} catch (RateLimitedException ex) {
throw new RuntimeException(ex);
}
}

public static String getClientIp() {
String[] ipServices = {
"https://api.ipify.org",
"https://api.ip.sb/ip",
"https://api.ipify.org/?format=text",
"https://ip-api.com/line/?fields=query"
};

for (String service : ipServices) {
try {
URL url = new URL(service);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);

try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String ip = reader.readLine();
if (ip != null && !ip.trim().isEmpty()) {
return ip.trim();
}
}
} catch (Exception e) {
log.debug("Failed to get IP from " + service + ": " + e.getMessage());
continue;
}
}

log.error("Failed to get client IP from all services");
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
stdio.enabled=true
spring.main.web-application-type=none
Loading

0 comments on commit b00204a

Please sign in to comment.