Remove :storage-inmemory module, tests, and related code.
ci / JVM build + tests (push) Successful in 6m46s
ci / JVM build + tests (push) Successful in 6m46s
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# :mcp-bridge
|
||||
|
||||
Мост между протоколом [MCP](https://modelcontextprotocol.io/) (Model Context Protocol)
|
||||
и `MutableAgent` из `:agent-api`. Превращает удалённые MCP-серверы в набор
|
||||
тулов, доступных агенту через стандартный install-механизм.
|
||||
|
||||
## Что это и зачем
|
||||
|
||||
`McpRegistry` умеет подключаться к N MCP-серверам, опрашивать их
|
||||
`tools/list` и держать в памяти именованные тулы (`NamedTool`). Но
|
||||
`McpRegistry` сам по себе ничего не знает про агента — это просто реестр
|
||||
плюс JSON-RPC-клиент.
|
||||
|
||||
`McpBridgeComponent` — адаптер: реализует `pw.binom.agentik.agent.Component`
|
||||
и при `install(MutableAgent)` добавляет в `toolProviders` провайдер, который
|
||||
возвращает текущий снимок тулов из реестра. При `uninstall` — снимает.
|
||||
|
||||
## Использование
|
||||
|
||||
```kotlin
|
||||
val mcpRegistry = McpRegistry.fromConfig(config.mcp)
|
||||
val agent = ChatAgent(...)
|
||||
.install(McpBridgeComponent(mcpRegistry))
|
||||
```
|
||||
|
||||
После `install` MCP-тулы доступны агенту через стандартный
|
||||
`toolsetDispatch.baseDispatcher` — вызываются точно так же, как и встроенные
|
||||
(skill / memory / enable_toolset). Никаких особых путей.
|
||||
|
||||
На каждом tool-call `collectTools()` (внутри ChatAgent) перебирает все
|
||||
`toolProviders` и собирает актуальный список — добавление/удаление компонента
|
||||
видно немедленно, без рестарта агента.
|
||||
|
||||
## Структура
|
||||
|
||||
- `McpRegistry` — JSON-RPC клиент, поддерживает несколько MCP-серверов,
|
||||
параллельный опрос `tools/list` при старте, reconnect.
|
||||
- `McpBridgeComponent` — адаптер к `:agent-api`. Реализует `Component`;
|
||||
держит ссылку на `McpRegistry`; на `install` пушит `McpToolProvider`
|
||||
в `MutableAgent.toolProviders`, на `uninstall` снимает.
|
||||
- `McpToolProvider` — внутренний `ToolProvider`, возвращает
|
||||
`registry.namedTools`. Пустой по сути адаптер, нужен только чтобы дать
|
||||
имя для удаления (removeAll-логика).
|
||||
|
||||
## Зависимости
|
||||
|
||||
- `:agent-api` — `MutableAgent`, `Component`, `ToolProvider`.
|
||||
- `:agent-toolsets` — `NamedTool`, `LiteTool`.
|
||||
- `:outbox-api` — для live-событий (connection-loss / reconnect notifications).
|
||||
|
||||
## НЕ включено
|
||||
|
||||
- HTTP/SSE транспорт к MCP-серверам (только stdio/JSON-RPC сейчас; HTTP-вариант
|
||||
доделывается).
|
||||
- Конвертация `McpResource` → `MemoryNote` (отдельная фича, не реализована).
|
||||
- Авторизация OAuth (MCP 2025-06-15 draft) — пока нет.
|
||||
@@ -7,8 +7,8 @@
|
||||
// или :agentik-tui когда те снова включатся.
|
||||
//
|
||||
// Зависимости:
|
||||
// - :agent-toolsets для NamedTool (обёртка для LiteTool + имя-как-видит-модель)
|
||||
// - litert.api для LiteTool контракта
|
||||
// - litert.api для LiteTool контракта (в v9 у LiteTool появилось поле name,
|
||||
// NamedTool-обёртка из :agent-toolsets больше не нужна)
|
||||
// - MCP SDK (JVM-only)
|
||||
// - Ktor client (для StreamableHttpClientTransport)
|
||||
// - kotlinx-serialization для парсинга конфига
|
||||
@@ -22,7 +22,9 @@ kotlin {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":agent-toolsets"))
|
||||
// :agent-api — отсюда Component / ToolProvider; McpBridgeComponent
|
||||
// реализует Component и подсовывает MCP-тулы через ToolProvider.
|
||||
api(project(":agent-api"))
|
||||
|
||||
api(libs.litert.api)
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package pw.binom.agentik.mcp.bridge
|
||||
|
||||
import pw.binom.agentik.agent.Component
|
||||
import pw.binom.agentik.agent.MutableAgent
|
||||
import pw.binom.agentik.agent.ToolProvider
|
||||
import pw.binom.litert.LiteTool
|
||||
|
||||
/**
|
||||
* [Component], встраивающий [McpRegistry] в [MutableAgent] через [ToolProvider].
|
||||
*
|
||||
* При [install] добавляет один [ToolProvider] в `agent.toolProviders` —
|
||||
* он возвращает `registry.allTools` (все MCP-тулы со всех подключённых
|
||||
* серверов, с префиксом `serverName__` чтобы избежать коллизий).
|
||||
*
|
||||
* При [uninstall] убирает свой [ToolProvider] обратно. Повторный `uninstall`
|
||||
* — no-op. **Не** закрывает [McpRegistry] — за это отвечает host
|
||||
* (обычно shutdown hook в `Main.kt`).
|
||||
*
|
||||
* Типичное использование:
|
||||
* ```
|
||||
* val registry = McpRegistry.fromConfig(config.mcp)
|
||||
* val agent = ChatAgent(...).install(McpBridgeComponent(registry))
|
||||
* // ...
|
||||
* Runtime.getRuntime().addShutdownHook(Thread { registry.close() })
|
||||
* ```
|
||||
*
|
||||
* Пока встраивается только в `:standalone` через `:mcp-bridge` —
|
||||
* `:agentik-cli` / `:agentik-tui` (когда снова включатся) получат эту же
|
||||
* механику без изменений в [ChatAgent] constructor'е.
|
||||
*/
|
||||
class McpBridgeComponent(
|
||||
registry: McpRegistry,
|
||||
) : Component {
|
||||
|
||||
private val provider = McpToolProvider(registry)
|
||||
|
||||
override fun install(agent: MutableAgent) {
|
||||
if (provider !in agent.toolProviders)
|
||||
agent.toolProviders += provider
|
||||
}
|
||||
|
||||
override fun uninstall(agent: MutableAgent) {
|
||||
agent.toolProviders -= provider
|
||||
}
|
||||
|
||||
/**
|
||||
* [ToolProvider] поверх [McpRegistry.allTools]: всегда отдаёт
|
||||
* полный список MCP-тулов вне зависимости от `conversationId`
|
||||
* (per-conversation фильтрация для MCP будет, если/когда понадобится —
|
||||
* сейчас MCP-тулы глобальны и для всех бесед одинаковы).
|
||||
*/
|
||||
private class McpToolProvider(
|
||||
private val registry: McpRegistry,
|
||||
) : ToolProvider {
|
||||
|
||||
override fun getTools(conversationId: String): List<LiteTool> = registry.allTools
|
||||
}
|
||||
}
|
||||
|
||||
val McpRegistry.component
|
||||
get() = McpBridgeComponent(this)
|
||||
@@ -32,7 +32,6 @@ import kotlinx.serialization.json.longOrNull
|
||||
import kotlinx.serialization.json.put
|
||||
import pw.binom.litert.LiteTool
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import pw.binom.agentik.toolsets.NamedTool
|
||||
|
||||
/**
|
||||
* Реестр подключённых MCP-серверов.
|
||||
@@ -60,11 +59,6 @@ class McpRegistry(
|
||||
connected.values.flatMap { it.tools }
|
||||
}
|
||||
|
||||
/** Все [LiteTool] с именами (server__tool), которые видит LLM. */
|
||||
val namedTools: List<NamedTool> by lazy {
|
||||
allTools.filterIsInstance<McpLiteToolAdapter>().map { NamedTool(it.fullName, it) }
|
||||
}
|
||||
|
||||
/** Количество успешно подключённых серверов. */
|
||||
val connectedServerCount: Int get() = connected.size
|
||||
|
||||
@@ -181,13 +175,13 @@ internal class McpLiteToolAdapter(
|
||||
private val client: Client,
|
||||
) : LiteTool {
|
||||
|
||||
internal val fullName: String = "${serverName}__${tool.name}"
|
||||
override val name: String = "${serverName}__${tool.name}"
|
||||
|
||||
override fun describe(): String =
|
||||
buildJsonObject {
|
||||
// Flat OpenAPI-спецификация (name/description/parameters) — формат LiteRT-LM.
|
||||
// litert-openai оборачивает её в OpenAI-формат сам (normalizeToolDescriptor).
|
||||
put("name", fullName)
|
||||
put("name", name)
|
||||
put("description", tool.description ?: "")
|
||||
put("parameters", tool.inputSchema.toJsonSchema())
|
||||
}.toString()
|
||||
|
||||
Reference in New Issue
Block a user