-
Notifications
You must be signed in to change notification settings - Fork 116
Used multiple chunks of WHO(not properly fomatted) to provide context to AI system #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,18 @@ import kotlinx.io.buffered | |
import kotlinx.serialization.json.JsonObject | ||
import kotlin.jvm.optionals.getOrNull | ||
|
||
import java.nio.file.Files | ||
import java.nio.file.Paths | ||
|
||
import java.io.File | ||
import io.modelcontextprotocol.kotlin.sdk.ReadResourceRequest | ||
import io.modelcontextprotocol.kotlin.sdk.TextResourceContents | ||
|
||
class MCPClient : AutoCloseable { | ||
// Configures using the `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` environment variables | ||
private val anthropic = AnthropicOkHttpClient.fromEnv() | ||
|
||
private val anthropic = AnthropicOkHttpClient.builder() | ||
.apiKey(System.getenv("ANTHROPIC_API_KEY") ) | ||
.build() | ||
|
||
// Initialize MCP client | ||
private val mcp: Client = Client(clientInfo = Implementation(name = "mcp-client-cli", version = "1.0.0")) | ||
|
@@ -36,10 +45,13 @@ class MCPClient : AutoCloseable { | |
return JsonValue.fromJsonNode(node) | ||
} | ||
|
||
// Connect to the server using the path to the server | ||
|
||
|
||
private val resourceContents: MutableMap<String, String> = mutableMapOf() | ||
|
||
suspend fun connectToServer(serverScriptPath: String) { | ||
try { | ||
// Build the command based on the file extension of the server script | ||
// Build command to start server | ||
val command = buildList { | ||
when (serverScriptPath.substringAfterLast(".")) { | ||
"js" -> add("node") | ||
|
@@ -50,19 +62,19 @@ class MCPClient : AutoCloseable { | |
add(serverScriptPath) | ||
} | ||
|
||
// Start the server process | ||
// Start server process | ||
val process = ProcessBuilder(command).start() | ||
|
||
// Setup I/O transport using the process streams | ||
// Setup transport | ||
val transport = StdioClientTransport( | ||
input = process.inputStream.asSource().buffered(), | ||
output = process.outputStream.asSink().buffered() | ||
) | ||
|
||
// Connect the MCP client to the server using the transport | ||
// Connect MCP client | ||
mcp.connect(transport) | ||
|
||
// Request the list of available tools from the server | ||
// List tools | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, it’s better to keep detailed comments in the example. For new users, a comment like |
||
val toolsResult = mcp.listTools() | ||
tools = toolsResult?.tools?.map { tool -> | ||
ToolUnion.ofTool( | ||
|
@@ -79,17 +91,56 @@ class MCPClient : AutoCloseable { | |
.build() | ||
) | ||
} ?: emptyList() | ||
|
||
println("Connected to server with tools: ${tools.joinToString(", ") { it.tool().get().name() }}") | ||
|
||
// // List all resources | ||
val resourcesResult = mcp.listResources() | ||
val resources = resourcesResult?.resources ?: emptyList() | ||
|
||
println("Found ${resources.size} resources from server.") | ||
|
||
for (resource in resources) { | ||
println("Loading resource: ${resource.name} (${resource.uri})") | ||
val readRequest = ReadResourceRequest(uri = resource.uri) | ||
val readResult = mcp.readResource(readRequest) | ||
|
||
val content = readResult?.contents | ||
?.filterIsInstance<TextResourceContents>() | ||
?.joinToString("\n") { it.text } | ||
if (content != null) { | ||
resourceContents[resource.uri] = content | ||
println("Successfully loaded resource (${content.length} characters) for URI: ${resource.uri}") | ||
} else { | ||
println("Warning: No content found for resource: ${resource.uri}") | ||
} | ||
} | ||
|
||
} catch (e: Exception) { | ||
println("Failed to connect to MCP server: $e") | ||
throw e | ||
} | ||
} | ||
|
||
private val messages = mutableListOf<MessageParam>() | ||
|
||
// Process a user query and return a string response | ||
suspend fun processQuery(query: String): String { | ||
// Create an initial message with a user's query | ||
val messages = mutableListOf( | ||
|
||
// / Prepend resources as SYSTEM message if they are loaded | ||
if (resourceContents.isNotEmpty()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] For clarity of role semantics, consider using MessageParam.Role.SYSTEM instead of ASSISTANT when prepending reference resources as context to the conversation. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
val combinedResourceText = resourceContents.values.joinToString("\n\n") | ||
messages.add( | ||
MessageParam.builder() | ||
.role(MessageParam.Role.ASSISTANT) // system role for context | ||
.content("Reference Resources:\n$combinedResourceText") | ||
.build() | ||
) | ||
} | ||
|
||
|
||
messages.add( | ||
MessageParam.builder() | ||
.role(MessageParam.Role.USER) | ||
.content(query) | ||
|
@@ -104,11 +155,76 @@ class MCPClient : AutoCloseable { | |
.build() | ||
) | ||
|
||
// val assistantReply = response.content().firstOrNull()?.text()?.getOrNull()?.text() | ||
|
||
|
||
// val finalText = mutableListOf<String>() | ||
// response.content().forEach { content -> | ||
// when { | ||
// // Append text outputs from the response | ||
// content.isText() -> finalText.add(content.text().getOrNull()?.text() ?: "") | ||
// | ||
// // If the response indicates a tool use, process it further | ||
// content.isToolUse() -> { | ||
// val toolName = content.toolUse().get().name() | ||
// val toolArgs = | ||
// content.toolUse().get()._input().convert(object : TypeReference<Map<String, JsonValue>>() {}) | ||
// | ||
// // Call the tool with provided arguments | ||
// val result = mcp.callTool( | ||
// name = toolName, | ||
// arguments = toolArgs ?: emptyMap() | ||
// ) | ||
// finalText.add("[Calling tool $toolName with args $toolArgs]") | ||
// | ||
// // Add the tool result message to the conversation | ||
// messages.add( | ||
// MessageParam.builder() | ||
// .role(MessageParam.Role.USER) | ||
// .content( | ||
// """ | ||
// "type": "tool_result", | ||
// "tool_name": $toolName, | ||
// "result": ${result?.content?.joinToString("\n") { (it as TextContent).text ?: "" }} | ||
// """.trimIndent() | ||
// ) | ||
// .build() | ||
// ) | ||
// | ||
// // Retrieve an updated response after tool execution | ||
// val aiResponse = anthropic.messages().create( | ||
// messageParamsBuilder | ||
// .messages(messages) | ||
// .build() | ||
// ) | ||
// | ||
// // Append the updated response to final text | ||
// finalText.add(aiResponse.content().first().text().getOrNull()?.text() ?: "") | ||
// } | ||
// } | ||
// } | ||
// | ||
// return finalText.joinToString("\n", prefix = "", postfix = "") | ||
// } | ||
Comment on lines
+158
to
+208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please leave a comment in the code explaining what this part does and why it is commented out |
||
val finalText = mutableListOf<String>() | ||
|
||
response.content().forEach { content -> | ||
when { | ||
// Append text outputs from the response | ||
content.isText() -> finalText.add(content.text().getOrNull()?.text() ?: "") | ||
content.isText() -> { | ||
val text = content.text().getOrNull()?.text() | ||
if (!text.isNullOrBlank()) { | ||
finalText.add(text) | ||
|
||
// Save assistant response to memory | ||
messages.add( | ||
MessageParam.builder() | ||
.role(MessageParam.Role.ASSISTANT) | ||
.content(text) | ||
.build() | ||
) | ||
} | ||
} | ||
|
||
// If the response indicates a tool use, process it further | ||
content.isToolUse() -> { | ||
|
@@ -121,19 +237,22 @@ class MCPClient : AutoCloseable { | |
name = toolName, | ||
arguments = toolArgs ?: emptyMap() | ||
) | ||
|
||
finalText.add("[Calling tool $toolName with args $toolArgs]") | ||
|
||
// Add the tool result message to the conversation | ||
// Add the tool_result to messages | ||
val toolResultContent = """ | ||
{ | ||
"type": "tool_result", | ||
"tool_name": "$toolName", | ||
"result": "${result?.content?.joinToString("\n") { (it as TextContent).text ?: "" }}" | ||
} | ||
""".trimIndent() | ||
|
||
messages.add( | ||
MessageParam.builder() | ||
.role(MessageParam.Role.USER) | ||
.content( | ||
""" | ||
"type": "tool_result", | ||
"tool_name": $toolName, | ||
"result": ${result?.content?.joinToString("\n") { (it as TextContent).text ?: "" }} | ||
""".trimIndent() | ||
) | ||
.content(toolResultContent) | ||
.build() | ||
) | ||
|
||
|
@@ -144,15 +263,27 @@ class MCPClient : AutoCloseable { | |
.build() | ||
) | ||
|
||
// Append the updated response to final text | ||
finalText.add(aiResponse.content().first().text().getOrNull()?.text() ?: "") | ||
val aiReply = aiResponse.content().firstOrNull()?.text()?.getOrNull()?.text() | ||
if (!aiReply.isNullOrBlank()) { | ||
finalText.add(aiReply) | ||
|
||
// Save assistant's new response after tool use | ||
messages.add( | ||
MessageParam.builder() | ||
.role(MessageParam.Role.ASSISTANT) | ||
.content(aiReply) | ||
.build() | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return finalText.joinToString("\n", prefix = "", postfix = "") | ||
} | ||
|
||
|
||
|
||
// Main chat loop for interacting with the user | ||
suspend fun chatLoop() { | ||
println("\nMCP Client Started!") | ||
|
@@ -173,4 +304,4 @@ class MCPClient : AutoCloseable { | |
anthropic.close() | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,34 @@ fun main(args: Array<String>) = runBlocking { | |
val client = MCPClient() | ||
client.use { | ||
client.connectToServer(serverPath) | ||
println("starting mcp client") | ||
client.chatLoop() | ||
} | ||
} | ||
} | ||
|
||
|
||
//fun main(args: Array<String>) = runBlocking { | ||
// if (args.isEmpty()) { | ||
// println("Please provide the path to the MCP server script as a command-line argument.") | ||
// return@runBlocking | ||
// } | ||
// | ||
// val serverScriptPath = args[0] | ||
// MCPClient().use { client -> | ||
// try { | ||
// client.connectToServer(serverScriptPath) | ||
// client.chatLoop() | ||
// } catch (e: Exception) { | ||
// println("Error: ${e.message}") | ||
// e.printStackTrace() | ||
// } | ||
// } | ||
//} | ||
|
||
|
||
//fun main() = runBlocking { | ||
// val client = MCPClient() | ||
// client.connectToServer(System.getenv("SERVER_PATH")!!) | ||
// client.chatLoop() | ||
//} | ||
|
||
Comment on lines
+15
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please leave only one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these changes necessary?
AnthropicOkHttpClient.fromEnv()
looks forANTHROPIC_API_KEY
in the environment variables by default