Initial commit
This commit is contained in:
parent
71ae106311
commit
7b32c3e3da
52 changed files with 7267 additions and 5 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -42,4 +42,8 @@ bin/
|
|||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
### Node.js ###
|
||||
node_modules
|
||||
dist
|
||||
|
|
|
|||
6
.idea/gradle.xml
generated
6
.idea/gradle.xml
generated
|
|
@ -1,11 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="gradleJvm" value="#JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
|||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<excludedPredefinedLibrary name="mcp-kotlin-sdk/src/test/resources/node-server/node_modules" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.0.21" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/misc.xml
generated
9
.idea/misc.xml
generated
|
|
@ -1,10 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ASMSmaliIdeaPluginConfiguration">
|
||||
<asm skipDebug="true" skipFrames="true" skipCode="false" expandFrames="false" />
|
||||
<groovy codeStyle="LEGACY" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
|||
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
||||
141
README.md
Normal file
141
README.md
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
# MCP Kotlin SDK
|
||||
|
||||
Kotlin implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), providing both client and server capabilities for integrating with LLM surfaces.
|
||||
|
||||
## Overview
|
||||
|
||||
The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Kotlin SDK implements the full MCP specification, making it easy to:
|
||||
|
||||
- Build MCP clients that can connect to any MCP server
|
||||
- Create MCP servers that expose resources, prompts and tools
|
||||
- Use standard transports like stdio, SSE, and WebSocket
|
||||
- Handle all MCP protocol messages and lifecycle events
|
||||
|
||||
## Installation
|
||||
|
||||
Add the JitPack repository to your build file:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
```
|
||||
|
||||
Add the dependency:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation("com.github.e5l:mcp-kotlin-sdk:main-SNAPSHOT")
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Creating a Client
|
||||
|
||||
```kotlin
|
||||
import org.jetbrains.kotlinx.mcp.client.Client
|
||||
import org.jetbrains.kotlinx.mcp.client.StdioClientTransport
|
||||
import org.jetbrains.kotlinx.mcp.Implementation
|
||||
|
||||
val client = Client(
|
||||
clientInfo = Implementation(
|
||||
name = "example-client",
|
||||
version = "1.0.0"
|
||||
)
|
||||
)
|
||||
|
||||
val transport = StdioClientTransport(
|
||||
inputStream = processInputStream,
|
||||
outputStream = processOutputStream
|
||||
)
|
||||
|
||||
// Connect to server
|
||||
client.connect(transport)
|
||||
|
||||
// List available resources
|
||||
val resources = client.listResources()
|
||||
|
||||
// Read a specific resource
|
||||
val resourceContent = client.readResource(
|
||||
ReadResourceRequest(uri = "file:///example.txt")
|
||||
)
|
||||
```
|
||||
|
||||
### Creating a Server
|
||||
|
||||
```kotlin
|
||||
import org.jetbrains.kotlinx.mcp.server.Server
|
||||
import org.jetbrains.kotlinx.mcp.server.ServerOptions
|
||||
import org.jetbrains.kotlinx.mcp.server.StdioServerTransport
|
||||
import org.jetbrains.kotlinx.mcp.ServerCapabilities
|
||||
|
||||
val server = Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(
|
||||
subscribe = true,
|
||||
listChanged = true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Add a resource
|
||||
server.addResource(
|
||||
uri = "file:///example.txt",
|
||||
name = "Example Resource",
|
||||
description = "An example text file",
|
||||
mimeType = "text/plain"
|
||||
) { request ->
|
||||
ReadResourceResult(
|
||||
contents = listOf(
|
||||
TextResourceContents(
|
||||
text = "This is the content of the example resource.",
|
||||
uri = request.uri,
|
||||
mimeType = "text/plain"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Start server with stdio transport
|
||||
val transport = StdioServerTransport()
|
||||
server.connect(transport)
|
||||
```
|
||||
|
||||
### Using SSE Transport
|
||||
|
||||
```kotlin
|
||||
import io.ktor.server.application.*
|
||||
import org.jetbrains.kotlinx.mcp.server.MCP
|
||||
|
||||
fun Application.module() {
|
||||
MCP {
|
||||
Server(
|
||||
serverInfo = Implementation(
|
||||
name = "example-sse-server",
|
||||
version = "1.0.0"
|
||||
),
|
||||
options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = null),
|
||||
resources = ServerCapabilities.Resources(subscribe = null, listChanged = null)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Issues and pull requests are welcome on GitHub at https://github.com/e5l/mcp-kotlin-sdk.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
import org.jetbrains.dokka.gradle.engine.parameters.VisibilityModifier
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.0.21"
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.dokka)
|
||||
}
|
||||
|
||||
group = "org.jetbrains.mcp"
|
||||
|
|
@ -10,12 +14,72 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.ktor.client.apache)
|
||||
implementation(libs.ktor.client.cio)
|
||||
implementation(libs.ktor.server.cio)
|
||||
implementation(libs.ktor.server.sse)
|
||||
implementation(libs.ktor.server.websockets)
|
||||
implementation(libs.kotlin.logging)
|
||||
|
||||
testImplementation(libs.kotlin.test)
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(libs.ktor.server.test.host)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotlinx.coroutines.debug)
|
||||
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
abstract class GenerateLibVersionTask @Inject constructor(
|
||||
@get:Input val libVersion: String,
|
||||
@get:OutputDirectory val sourcesDir: File
|
||||
) : DefaultTask() {
|
||||
@TaskAction
|
||||
fun generate() {
|
||||
val sourceFile = File(sourcesDir, "LibVersion.kt")
|
||||
|
||||
sourceFile.writeText(
|
||||
"""
|
||||
package shared
|
||||
|
||||
const val LIB_VERSION = "$libVersion"
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dokka {
|
||||
moduleName.set("MCP Kotlin SDK")
|
||||
|
||||
dokkaSourceSets.main {
|
||||
sourceLink {
|
||||
localDirectory.set(file("src/main/kotlin"))
|
||||
remoteUrl("https://github.com/e5l/mcp-kotlin-sdk")
|
||||
remoteLineSuffix.set("#L")
|
||||
documentedVisibilities(VisibilityModifier.Public)
|
||||
}
|
||||
}
|
||||
dokkaPublications.html {
|
||||
outputDirectory.set(project.layout.projectDirectory.dir("docs"))
|
||||
}
|
||||
}
|
||||
|
||||
val sourcesDir = File(project.layout.buildDirectory.asFile.get(), "generated-sources/libVersion")
|
||||
|
||||
val generateLibVersionTask =
|
||||
tasks.register<GenerateLibVersionTask>("generateLibVersion", version.toString(), sourcesDir)
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
kotlin.srcDir(generateLibVersionTask.map { it.sourcesDir })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,4 @@
|
|||
kotlin.code.style=official
|
||||
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
|
||||
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
|
||||
|
|
|
|||
37
gradle/libs.versions.toml
Normal file
37
gradle/libs.versions.toml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
[versions]
|
||||
# plugins version
|
||||
kotlin = "2.0.21"
|
||||
dokka = "2.0.0-Beta"
|
||||
|
||||
# libraries version
|
||||
serialization = "1.7.3"
|
||||
coroutines = "1.9.0"
|
||||
ktor = "3.0.2"
|
||||
mockk = "1.13.13"
|
||||
logging = "7.0.0"
|
||||
|
||||
[libraries]
|
||||
# Kotlinx libraries
|
||||
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
|
||||
kotlin-logging = { group = "io.github.oshai", name = "kotlin-logging-jvm", version.ref = "logging" }
|
||||
|
||||
# Ktor
|
||||
ktor-client-apache = { group = "io.ktor", name = "ktor-client-apache", version.ref = "ktor" }
|
||||
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
|
||||
ktor-server-sse = { group = "io.ktor", name = "ktor-server-sse", version.ref = "ktor" }
|
||||
ktor-server-websockets = { group = "io.ktor", name = "ktor-server-websockets", version.ref = "ktor" }
|
||||
ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio", version.ref = "ktor" }
|
||||
|
||||
# Testing
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
kotlinx-coroutines-debug = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-debug", version.ref = "coroutines" }
|
||||
ktor-server-test-host = { group = "io.ktor", name = "ktor-server-test-host", version.ref = "ktor" }
|
||||
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
|
||||
|
||||
|
||||
|
||||
[plugins]
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
297
samples/cli.kt
Normal file
297
samples/cli.kt
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
package org.jetbrains.kotlinx.mcp
|
||||
|
||||
import org.jetbrains.kotlinx.mcp.client.Client
|
||||
import org.jetbrains.kotlinx.mcp.client.StdioClientTransport
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.cio.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.sse.*
|
||||
import io.ktor.util.collections.*
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.jetbrains.kotlinx.mcp.server.MCP
|
||||
import org.jetbrains.kotlinx.mcp.server.SSEServerTransport
|
||||
import org.jetbrains.kotlinx.mcp.server.Server
|
||||
import org.jetbrains.kotlinx.mcp.server.ServerOptions
|
||||
import org.jetbrains.kotlinx.mcp.server.StdioServerTransport
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
if (args.isEmpty())
|
||||
return
|
||||
val first = args[0]
|
||||
when (first) {
|
||||
"--server" -> runServer()
|
||||
"--demo" -> runDemo()
|
||||
"--sse-server" -> {
|
||||
if (args.size < 2) {
|
||||
System.err.println("Missing port argument")
|
||||
return
|
||||
}
|
||||
val port = args[1].toIntOrNull()
|
||||
if (port == null) {
|
||||
System.err.println("Invalid port: ${args[1]}")
|
||||
return
|
||||
}
|
||||
runSseServer(port)
|
||||
}
|
||||
|
||||
else -> {
|
||||
System.err.println("Unknown argument: $first")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runDemo() {
|
||||
val processBuilder = ProcessBuilder("npx", "-y", "@jetbrains/mcp-proxy")
|
||||
|
||||
var process: Process? = null
|
||||
try {
|
||||
process = processBuilder.start()
|
||||
|
||||
val client = Client(
|
||||
Implementation("test", "1.0"),
|
||||
)
|
||||
val transport = StdioClientTransport(process.inputStream, process.outputStream)
|
||||
runBlocking {
|
||||
client.connect(transport)
|
||||
|
||||
val serverCapabilities = client.getServerCapabilities()
|
||||
|
||||
// Resources capability check
|
||||
serverCapabilities?.resources?.let {
|
||||
try {
|
||||
val resources = client.listResources()
|
||||
System.err.println("Resources: ${resources?.resources?.joinToString { it.name }}")
|
||||
} catch (e: Exception) {
|
||||
System.err.println("Failed to list resources: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Tools capability check
|
||||
serverCapabilities?.tools?.let {
|
||||
try {
|
||||
val terminal = client.callTool("execute_terminal_command", mapOf("command" to "ls"))
|
||||
System.err.println(terminal?.content?.first())
|
||||
|
||||
val tools = client.listTools()
|
||||
System.err.println(tools?.tools?.joinToString(", ") { tool -> tool.name })
|
||||
|
||||
// tools?.tools?.reversed()?.find { it.name == "toggle_debugger_breakpoint" }?.let { org.jetbrains.kotlinx.mcp.callTool(client, it) }
|
||||
|
||||
tools?.tools?.reversed()?.forEachIndexed { i, tool ->
|
||||
System.err.println("$i out of ${tools.tools.size}: ${tool.name}")
|
||||
callTool(client, tool)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
System.err.println("Failed to list tools: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Prompts capability check
|
||||
serverCapabilities?.prompts?.let {
|
||||
try {
|
||||
val prompts = client.listPrompts()
|
||||
System.err.println("Prompts: ${prompts?.prompts?.joinToString { it.name }}")
|
||||
} catch (e: Exception) {
|
||||
System.err.println("Failed to list prompts: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
process?.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun callTool(client: Client, tool: Tool) {
|
||||
System.err.println(tool.inputSchema)
|
||||
|
||||
val map = fillSchema(tool.inputSchema)
|
||||
|
||||
System.err.println("calling: ${tool.name}: $map")
|
||||
val result = try {
|
||||
client.callTool(CallToolRequest(tool.name, map))
|
||||
} catch (e: Exception) {
|
||||
System.err.println("Failed to call tool ${tool.name}: ${e.message}")
|
||||
return
|
||||
}
|
||||
System.err.println("Result: ${result?.content?.first()}\n")
|
||||
}
|
||||
|
||||
private fun fillSchema(schema: Tool.Input): JsonObject {
|
||||
return buildJsonObject {
|
||||
schema.properties.forEach { name, elt ->
|
||||
val type = (elt.jsonObject["type"] as JsonPrimitive).content
|
||||
val value = when (type) {
|
||||
"string" -> JsonPrimitive("Hello")
|
||||
"number" -> JsonPrimitive(42)
|
||||
"boolean" -> JsonPrimitive(true)
|
||||
else -> {
|
||||
System.err.println("+".repeat(30) + " Unknown type: $type " + "+".repeat(30))
|
||||
JsonPrimitive("Unknown")
|
||||
}
|
||||
}
|
||||
put(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun runServer() {
|
||||
val def = CompletableDeferred<Unit>()
|
||||
|
||||
val server = Server(
|
||||
Implementation(
|
||||
name = "mcp-kotlin test server",
|
||||
version = "0.1.0"
|
||||
),
|
||||
ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = true),
|
||||
resources = ServerCapabilities.Resources(subscribe = true, listChanged = true),
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
)
|
||||
),
|
||||
onCloseCallback = {
|
||||
def.complete(Unit)
|
||||
}
|
||||
)
|
||||
|
||||
server.addPrompt(
|
||||
name = "Kotlin Developer",
|
||||
description = "Develop small kotlin applications",
|
||||
arguments = listOf(
|
||||
PromptArgument(
|
||||
name = "Project Name",
|
||||
description = "Project name for the new project",
|
||||
required = true
|
||||
)
|
||||
)
|
||||
) { request ->
|
||||
GetPromptResult(
|
||||
"Description for ${request.name}",
|
||||
messages = listOf(
|
||||
PromptMessage(
|
||||
role = Role.user,
|
||||
content = TextContent("Develop a kotlin project named <name>${request.arguments?.get("Project Name")}</name>")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Add a tool
|
||||
server.addTool(
|
||||
name = "Test org.jetbrains.kotlinx.mcp.Tool",
|
||||
description = "A test tool",
|
||||
inputSchema = Tool.Input()
|
||||
) { request ->
|
||||
CallToolResult(
|
||||
content = listOf(TextContent("Hello, world!"))
|
||||
)
|
||||
}
|
||||
|
||||
// Add a resource
|
||||
server.addResource(
|
||||
uri = "https://google.com/",
|
||||
name = "Google Search",
|
||||
description = "Web search engine",
|
||||
mimeType = "text/html"
|
||||
) { request ->
|
||||
ReadResourceResult(
|
||||
contents = listOf(
|
||||
TextResourceContents("Placeholder content for ${request.uri}", request.uri, "text/html")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Note: The server will handle listing prompts, tools, and resources automatically.
|
||||
// The handleListResourceTemplates will return empty as defined in the Server code.
|
||||
|
||||
val transport = StdioServerTransport()
|
||||
|
||||
val err = System.err
|
||||
|
||||
runBlocking {
|
||||
server.connect(transport)
|
||||
err.println("Server running on stdio")
|
||||
def.await()
|
||||
}
|
||||
|
||||
err.println("Server closed")
|
||||
}
|
||||
|
||||
fun runSseServer(port: Int): Unit = runBlocking {
|
||||
val servers = ConcurrentMap<String, Server>()
|
||||
|
||||
embeddedServer(CIO, host = "0.0.0.0", port = port) {
|
||||
install(SSE)
|
||||
routing {
|
||||
sse("/sse") {
|
||||
val transport = SSEServerTransport("/message", this)
|
||||
val options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = null),
|
||||
resources = ServerCapabilities.Resources(subscribe = null, listChanged = null),
|
||||
)
|
||||
)
|
||||
val server = Server(
|
||||
Implementation(
|
||||
name = "mcp-kotlin test server",
|
||||
version = "0.1.0"
|
||||
),
|
||||
options
|
||||
)
|
||||
|
||||
// For SSE, you can also add prompts/tools/resources if needed:
|
||||
// server.addTool(...), server.addPrompt(...), server.addResource(...)
|
||||
|
||||
servers[transport.sessionId] = server
|
||||
|
||||
server.onCloseCallback = {
|
||||
println("Server closed")
|
||||
servers.remove(transport.sessionId)
|
||||
}
|
||||
|
||||
server.connect(transport)
|
||||
}
|
||||
post("/message") {
|
||||
println("Received Message")
|
||||
val sessionId: String = call.request.queryParameters["sessionId"]!!
|
||||
val transport = servers[sessionId]?.transport as? SSEServerTransport
|
||||
if (transport == null) {
|
||||
call.respond(HttpStatusCode.NotFound, "Session not found")
|
||||
return@post
|
||||
}
|
||||
|
||||
transport.handlePostMessage(call)
|
||||
}
|
||||
}
|
||||
}.start(wait = true)
|
||||
}
|
||||
|
||||
fun runSseServerKtor(port: Int): Unit = runBlocking {
|
||||
embeddedServer(CIO, host = "0.0.0.0", port = port) {
|
||||
MCP {
|
||||
val options = ServerOptions(
|
||||
capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = null),
|
||||
resources = ServerCapabilities.Resources(subscribe = null, listChanged = null),
|
||||
)
|
||||
)
|
||||
|
||||
Server(
|
||||
Implementation(
|
||||
name = "mcp-kotlin test server",
|
||||
version = "0.1.0"
|
||||
),
|
||||
options
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
samples/connect-to-intellij.sh
Executable file
3
samples/connect-to-intellij.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
../gradlew clean jar; java -jar ../build/libs/mcp-kotlin-sdk-1.0-SNAPSHOT.jar --demo
|
||||
3
samples/inspector.sh
Executable file
3
samples/inspector.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
../gradlew clean jar && npx @modelcontextprotocol/inspector java -jar ../build/libs/mcp-kotlin-sdk-1.0-SNAPSHOT.jar --server
|
||||
3
samples/node-server/build.sh
Executable file
3
samples/node-server/build.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
pnpm build
|
||||
528
samples/node-server/package-lock.json
generated
Normal file
528
samples/node-server/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
{
|
||||
"name": "node-server",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.0.3",
|
||||
"node-fetch": "^3.3.2"
|
||||
},
|
||||
"bin": {
|
||||
"node-server": "dist/server.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"shx": "^0.3.4",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.3.tgz",
|
||||
"integrity": "sha512-2as3cX/VJ0YBHGmdv3GFyTpoM8q2gqE98zh3Vf1NwnsSY0h3mvoO07MUzfygCKkWsFjcZm4otIiqD6Xh7kiSBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz",
|
||||
"integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
|
||||
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
|
||||
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.6.3",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||
"integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.13.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/shelljs": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
|
||||
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"glob": "^7.0.0",
|
||||
"interpret": "^1.0.0",
|
||||
"rechoir": "^0.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"shjs": "bin/shjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/shx": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
|
||||
"integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.3",
|
||||
"shelljs": "^0.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"shx": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
|
||||
"integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
||||
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
samples/node-server/package.json
Normal file
26
samples/node-server/package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"main": "dist/server.js",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"node-server": "dist/server.js"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc --outDir dist && shx chmod +x dist/*.js",
|
||||
"prepare": "npm run build",
|
||||
"watch": "tsc --watch --outDir dist",
|
||||
"run_server": "dist/server.js",
|
||||
"run_client": "dist/client.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.0.3",
|
||||
"node-fetch": "^3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"shx": "^0.3.4",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
2
samples/node-server/run_client.sh
Executable file
2
samples/node-server/run_client.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
./dist/client.js
|
||||
2
samples/node-server/run_server.sh
Executable file
2
samples/node-server/run_server.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
./dist/server.js
|
||||
52
samples/node-server/src/client.ts
Normal file
52
samples/node-server/src/client.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import * as net from 'net';
|
||||
import { Readable, Writable } from 'stream';
|
||||
|
||||
const client = new net.Socket();
|
||||
const PORT = 3000;
|
||||
const HOST = '127.0.0.1';
|
||||
|
||||
// If startClient is supposed to be asynchronous and do something later,
|
||||
// keep async. Otherwise, remove async if not needed.
|
||||
function startClient(input: Readable, output: Writable) {
|
||||
// TODO: Implement client logic here if needed
|
||||
// For now, it's just a placeholder.
|
||||
}
|
||||
|
||||
const input = new Readable({
|
||||
read() {
|
||||
// No-op: We'll push data into this stream externally.
|
||||
}
|
||||
});
|
||||
|
||||
const output = new Writable({
|
||||
write(chunk, encoding, callback) {
|
||||
console.log('Writing to output (and pushing to input):', chunk.toString('utf-8'));
|
||||
input.push(chunk);
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
client.connect(PORT, HOST, () => {
|
||||
console.log('Connected to server');
|
||||
startClient(input, output);
|
||||
});
|
||||
|
||||
client.on('data', (data) => {
|
||||
// When we receive data from the server socket, push it into `input`.
|
||||
input.push(data);
|
||||
});
|
||||
|
||||
client.on('close', () => {
|
||||
input.push(null); // Signal the end of the input stream
|
||||
console.log('Connection closed by server');
|
||||
});
|
||||
|
||||
client.on('error', (err) => {
|
||||
console.error('Client error:', err);
|
||||
});
|
||||
|
||||
// Push initial data into `input`. If no reader is attached, this might not be visible.
|
||||
// Consider adding `input.on('data', ...)` somewhere else if you want to see this data.
|
||||
input.push("Hello");
|
||||
95
samples/node-server/src/server.ts
Executable file
95
samples/node-server/src/server.ts
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import * as net from 'net';
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { Readable, Writable, WritableOptions } from 'stream';
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
const PORT = 3000;
|
||||
|
||||
const mcpServer = new Server(
|
||||
{
|
||||
name: "jetbrains/proxy",
|
||||
version: "0.1.0",
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {
|
||||
listChanged: true,
|
||||
},
|
||||
resources: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
console.log("ListToolsRequest received");
|
||||
return {
|
||||
tools: []
|
||||
};
|
||||
});
|
||||
|
||||
async function handleConnection(stdin: Readable, stdout: Writable) {
|
||||
const transport = new StdioServerTransport(stdin, stdout);
|
||||
try {
|
||||
await mcpServer.connect(transport);
|
||||
} catch (err) {
|
||||
console.error('Error connecting MCP server:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// A Writable stream class that writes data back to the socket
|
||||
class SocketWritable extends Writable {
|
||||
private socket: net.Socket;
|
||||
|
||||
constructor(socket: net.Socket, options?: WritableOptions) {
|
||||
super(options);
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void) {
|
||||
console.log("Socket write:", chunk.toString());
|
||||
this.socket.write(chunk, encoding, callback);
|
||||
}
|
||||
}
|
||||
|
||||
const server = net.createServer((socket) => {
|
||||
console.log('Client connected');
|
||||
|
||||
// Create a Readable stream that we will push data into from the socket
|
||||
const stdin = new Readable({
|
||||
read() {
|
||||
// We'll use stdin.push() from socket data events
|
||||
},
|
||||
});
|
||||
|
||||
// Create a Writable that writes back to the socket
|
||||
const stdout = new SocketWritable(socket);
|
||||
|
||||
socket.on('data', (chunk) => {
|
||||
const decoded = chunk.toString("utf-8");
|
||||
console.log("Socket data:", decoded);
|
||||
// Push the raw buffer into stdin so the server can read it
|
||||
stdin.push(chunk);
|
||||
});
|
||||
|
||||
socket.on('end', () => {
|
||||
stdin.push(null); // Signal EOF to the stdin stream
|
||||
console.log('Client disconnected');
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
});
|
||||
|
||||
handleConnection(stdin, stdout);
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`MCP proxy server listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
console.error('Server error:', err);
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue