-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
422 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>" | ||
} | ||
} | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
16 changes: 16 additions & 0 deletions
16
jcommon/mcp/mcp-ipinfo/src/main/java/run/mone/mcp/ipinfo/IpinfoMcpBootstrap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
jcommon/mcp/mcp-ipinfo/src/main/java/run/mone/mcp/ipinfo/config/IpinfoConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
jcommon/mcp/mcp-ipinfo/src/main/java/run/mone/mcp/ipinfo/function/IpinfoFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
jcommon/mcp/mcp-ipinfo/src/main/java/run/mone/mcp/ipinfo/server/IpinfoMcpServer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
jcommon/mcp/mcp-ipinfo/src/main/java/run/mone/mcp/ipinfo/service/IpinfoService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
jcommon/mcp/mcp-ipinfo/src/main/resources/application.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
stdio.enabled=true | ||
spring.main.web-application-type=none |
Oops, something went wrong.