快速mcp标量
Scala 3 for MCP:JVM和Scala.js/Bun上的注释驱动和类型化合约API。
fast mcp-scala是一个开发人员友好的库,用于构建 模型上下文协议 服务器。扩展一个特征,声明你的工具,完成:
object HelloWorld extends McpServerApp[Stdio, HelloWorld.type]:
@Tool(name = Some("add"))
def add(@Param("a") a: Int, @Param("b") b: Int): Int = a + b不 override def run,没有 import zio.*,没有仪式。两条互补的注册路径汇聚在同一后端:
@Tool/@Resource/@Prompt注释+scanAnnotations[T]为了获得零样板、宏驱动的体验(JVM+Scala.js/Bun)McpTool,McpPrompt,McpStaticResource,McpTemplateResource对于一流的、可测试的、跨平台的合约值,处理程序返回纯值,ZIO,Either[Throwable, _],或Try通过ToHandlerEffect类型类
建立在 齐奥2, 貘-派生模式, 杰克逊3 (合资企业)/ 齐奥·杰森 (JS),官方 Java MCP SDK 1.1.1和官方 TS MCP SDK 1.29.0传输是一个幻影类型的参数-- McpServerApp[Stdio, Self.type] 或 McpServerApp[Http, Self.type] --使用编译时运行程序调度。
目录
- 安装
- 快速入门
- 选择注册路径
- 工具和
@Param元数据 - 工具提示
- 资源(静态和模板化)
- 提示
- 背景(
McpContext) - 运输
- 自定义解码(杰克逊3)
- 两个后端,一个API
- 规格覆盖范围
- 运行示例
- Claude桌面集成
- 在当地发展
安装
// JVM — Java SDK-backed runtime with annotations, derived schemas, HTTP + stdio transports.
libraryDependencies += "com.tjclp" %% "fast-mcp-scala" % "0.3.2"
// Scala.js — TS SDK-backed runtime on Bun/Node + the same annotation and typed-contract APIs.
libraryDependencies += "com.tjclp" %%% "fast-mcp-scala" % "0.3.2"基于Scala 3.8.3构建。JVM需要JDK 17+。Scala.js工件发布于 sjs1_3 (Scala.js 1.x);运行在Bun(一级)和Node 18+上。
快速入门
使用一个工具的单个文件服务器——相同的代码存在于 HelloWorld.scala:
//> using scala 3.8.3
//> using dep com.tjclp::fast-mcp-scala:0.3.2
//> using options "-Xcheck-macros" "-experimental"
import com.tjclp.fastmcp.{*, given}
object HelloWorld extends McpServerApp[Stdio, HelloWorld.type]:
@Tool(name = Some("add"), description = Some("Add two numbers"), readOnlyHint = Some(true))
def add(@Param("First operand") a: Int, @Param("Second operand") b: Int): Int = a + b就是这样——不 import zio.*,没有 override def run,没有 ZIO.succeed(...)The McpServerApp[T, Self] trait处理服务器构建、注释扫描和传输生命周期。传输是一个幻影类型的参数(Stdio / Http)编译时选择运行程序。
通过MCP检查员进行练习:
npx @modelcontextprotocol/inspector scala-cli scripts/quickstart.sc选择注册路径
注释(@Tool + scanAnnotations) | 类型化合同(McpTool) | |
|---|---|---|
| 平台 | JVM+Scala.js/Bun | JVM&Scala.js/Bun |
| 样式 | 对象上的方法,由宏发现 | 第一类 vals |
| 模式 | 从方法签名派生& @Param | 源自案例类字段& @Param |
| 测试 | 直接调用方法 | 调用 .handler 关于价值 |
| 可组合性 | 对象公开的任何方法 | 收集到列表中,从配置生成 |
| 最适合 | 快速服务器、原型、单模块应用程序 | 库、跨模块共享、生产代码库 |
两者共存于同一服务器上--override tools / prompts / staticResources / templateResources 在你的 McpServerApp 将类型化合约与带注释的方法一起挂载:
object MyServer extends McpServerApp[Stdio, MyServer.type]:
@Tool(name = Some("ping")) def ping(): String = "pong"
override val tools = List(
McpTool[AddArgs, AddResult](name = "add") { args =>
AddResult(args.a + args.b) // plain value — auto-lifted
}
)处理程序lambdas返回纯值, ZIO, Either[Throwable, _],或 scala.util.Try --the ToHandlerEffect[F[_]] typeclass选对了电梯。带上你自己的其他效果系统(cats.effect.IO莫妮克斯。..).
看 AnnotatedServer.scala 对于注释路径和 ContractServer.scala 用于打印合同。
工具和 @Param 元数据
每个工具参数都可以携带流入派生JSON模式的元数据:
@Tool(name = Some("search"), description = Some("Search with optional filters"))
def search(
@Param(description = "Search query", examples = List("scala", "mcp"))
query: String,
@Param(description = "Maximum results", examples = List("10", "25"), required = false)
limit: Option[Int],
@Param(
description = "Sort order",
schema = Some("""{"type": "string", "enum": ["relevance", "date"]}""")
)
sortBy: String
): String = ???description--填充模式的description领域examples--填充JSON模式examples数组(客户端可以显示建议)required = false--结合Option[...]或默认值,将该字段标记为可选schema--完全覆盖派生模式的原始JSON模式片段(适用于Scala类型无法表达的枚举约束、模式或数值边界)
完整演示 AnnotatedServer.scala.
工具提示
MCP工具注释(也称为行为提示)告诉客户端工具的行为。启动它们 @Tool:
| 提示 | 含义 |
|---|---|
title | 人类可读的显示名称(与导线级别不同 name) |
readOnlyHint | 该工具仅读取状态;无需确认即可拨打电话 |
destructiveHint | 该工具可能会不可逆地修改状态——客户端应确认 |
idempotentHint | 使用相同参数的重复调用会产生与一次调用相同的效果 |
openWorldHint | 该工具到达本地进程之外(网络、文件系统、API) |
returnDirect | 将结果直接返回给用户,跳过LLM后处理 |
@Tool(
name = Some("listTasks"),
description = Some("List tasks with optional filtering"),
readOnlyHint = Some(true),
idempotentHint = Some(true),
openWorldHint = Some(false)
)
def listTasks(filter: TaskFilter): List[Task] = ...看 TaskManagerServer.scala 以获取现实工具集的提示。
资源(静态和模板化)
静态资源有一个固定的URI,没有参数:
@Resource(uri = "static://welcome", description = Some("A welcome message"))
def welcome(): String = "Welcome!"模板化资源使用 {placeholders} 在URI中,与方法参数名称匹配:
@Resource(
uri = "users://{userId}/profile",
description = Some("User profile as JSON"),
mimeType = Some("application/json")
)
def userProfile(@Param("The user id") userId: String): String = ...提示
返回a List[Message] --快速mcp-scala处理mcp成帧:
@Prompt(name = Some("greeting"), description = Some("Personalized greeting"))
def greeting(
@Param("Name of the person") name: String,
@Param("Optional title", required = false) title: String = ""
): List[Message] =
List(Message(Role.User, TextContent(s"Generate a warm greeting for $title $name.")))返回单个值的提示 String 自动包装成 User 消息。
背景(McpContext)
添加可选 ctx: McpContext (注释路径)或使用 McpTool.contextual (键入合约路径)访问客户声明的信息和功能:
def echo(args: Map[String, Any], ctx: Option[McpContext]): String =
val clientName = ctx.flatMap(_.getClientInfo.map(_.name())).getOrElse("unknown")
s"Hello from $clientName"可运行演示: ContextEchoServer.scala.
运输
传输是上的幻影类型参数 McpServerApp[T, Self] — Stdio 或 Http.匹配 TransportRunner[T] 给定在编译时解析,因此用户代码中没有运行时传输管道。
stdio(适用于克劳德桌面、MCP检查器)
object MyServer extends McpServerApp[Stdio, MyServer.type]:
@Tool(...) def hello(name: String): String = s"Hello, $name!"HTTP(用于远程客户端、负载均衡器、测试工具)
翻到 Http 并覆盖 settings 调整听众。 runHttp() 提供完整的MCP流式HTTP规范: POST /mcp 对于JSON-RPC mcp-session-id 用于会话跟踪的标头和用于长时间运行的调用的SSE流。
object MyHttpServer extends McpServerApp[Http, MyHttpServer.type]:
override def settings = McpServerSettings(port = 8090)
@Tool(...) def hello(name: String): String = s"Hello, $name!"切换 stateless = true 上 McpServerSettings 对于仅请求/响应模式(无会话,无SSE),在负载均衡器后面很有用。
需要较低级别的控制?跳过糖的特性,直接构建-- val server = McpServer("name", "0.1.0") 返回适合平台的服务器,您可以调用 .tool(...) / .runHttp() 你自己在你自己的 ZIOAppDefault.
| 设置 | 默认值 | 说明 |
|---|---|---|
host | 0.0.0.0 | 绑定地址 |
port | 8000 | 监听端口 |
httpEndpoint | /mcp | JSON-RPC端点路径 |
stateless | false | 禁用会话和SSE |
两种模式的卷发食谱都在 HttpServer.scala.
任务(实验性,默认关闭)
MCP任务(规范 2025-11-25)包装长时间运行 tools/call 在持久的轮询状态机中调用。客户端发送 params.task: {ttl},得到一个 CreateTaskResult 立即,然后投票 tasks/get / tasks/list / tasks/cancel / tasks/result 直到完成。适用于LLM批处理作业、昂贵的计算以及与外部作业API的集成,否则这些API将在请求/响应下超时。
启用每台服务器(默认情况下为关闭状态——规范标记为任务实验):
val server = McpServer(
name = "my-server",
settings = McpServerSettings(tasks = TaskSettings(enabled = true))
)按工具选择加入——注释路径:
@Tool(name = Some("expensive-op"), taskSupport = Some("optional"))
def expensiveOp(@Param("input") x: String): String = ???按工具类型选择加入合同路径:
val tool = McpTool[Args, Result](name = "expensive-op")(args => work(args))
.withTaskSupport(TaskSupport.Optional)taskSupport 值: "forbidden" (默认), "optional" (客户可以增加), "required" (客户端必须--裸电话返回 -32601).
运输限制:任务在fast mcp-scala自己的ZIO HTTP传输中分派,因为上游Java mcp SDK 1.1.1尚未实现它们。因此:
- Java虚拟机:正在进行
runHttp()和stateless = false(默认的流式传输)。runStdio()如果出现以下情况,无状态HTTP在启动时会很快失败tasks.enabled这是真的。 - JS/Bun:同时适用于有状态和无状态
runHttp().runStdio()失败得很快。
启用后 tasks 能力在 initialize 每个可选择的工具表面 execution.taskSupport 上 tools/list.
自定义解码(杰克逊3)
fast-mcp-scala使用Jackson 3将原始JSON-RPC参数转换为scala值。原语、Scala枚举、case类, Option, List, Map,以及 java.time 类型可以开箱即用,无需配置。
对于自定义导线格式,请提供 given JacksonConverter[T]:
import java.time.LocalDateTime
given JacksonConverter[LocalDateTime] = JacksonConverter.fromPartialFunction[LocalDateTime] {
case s: String => LocalDateTime.parse(s)
}
given JacksonConverter[Task] = DeriveJacksonConverter.derived[Task]处理程序收到 JacksonConversionContext (不是原始的杰克逊映射器)——参见 docs/jackson-converter-enhancements.md API的详细信息。
两个后端,一个API
fast-mcps-cala是一个单独的库,在同一个共享抽象API后面有两个真正的运行时后端JVM和scala.js/Bun:
shared/src/ (platform-neutral Scala 3)
┌──────────────────────────────────────────────────────────┐
│ annotations │ typed contracts │ Tool/Prompt/Resource │
│ (@Tool, ...)│ (McpTool, McpPrompt)│ managers + McpContext│
│ McpServerApp │ scanAnnotations │ TransportRunner │
│ (sugar trait)│ (shared macros) │ ToHandlerEffect │
└──────────┬─────────────────────────────┬─────────────────┘
│ │
jvm/src/ (FastMcpServer) js/src/ (JsMcpServer)
wraps Java MCP SDK wraps TS MCP SDK via
(mcp-core 1.1.1) Scala.js facades, runs on BunMcpServerApp[T, Self] 是两个目标的声明性入口点;具体后端通过 McpServerCoreFactory 给定(FastMcpServer 在JVM上, JsMcpServer 关于JS)。类型化合同(McpTool, McpPrompt, McpStaticResource, McpTemplateResource)在两者上编译和挂载时保持不变。
Scala.js后端为您提供了什么:
- 真正的MCP 服务器运行时 在Bun,包裹官员
@modelcontextprotocol/sdk- stdio (runStdio)和流式HTTP(runHttp)传输,具有有状态(会话+SSE)和无状态(仅JSON响应)模式。 - 基于AJV的工具参数模式验证,与JVM服务器的行为相匹配。
JsMcpContext扩展方法(getClientInfo,getClientCapabilities,getSessionId)对于需要客户端会话详细信息的处理程序。
当前平台平价:
| 能力 | JVM | Scala.js(Bun优先) |
|---|---|---|
McpServerApp[T, Self] 糖特性 | ✅ | ✅ |
@Tool / @Resource / @Prompt + scanAnnotations[T] | ✅ | ✅ |
类型化合同(McpTool, McpPrompt, McpStaticResource, McpTemplateResource) | ✅ | ✅ |
ToolSchemaProvider[A] 自动推导 @Param | ✅ 通过Tapir✅ 通过Tapir | |
ToHandlerEffect[F] --普通值/ZIO/要么/尝试 | ✅ | ✅ |
| 标准运输 | ✅ (Java SDK) | ✅ (TS SDK) |
| 流式HTTP——有状态(会话+SSE) | ✅ (ZIO HTTP) | ✅ (Bun.serve+Web标准传输) |
| 流式HTTP--无状态 | ✅ | ✅ |
| 自定义解码器 | ✅ JacksonConverter | ✅ given JsonDecoder[T] → McpDecoder[T] 通过zio json |
HTTP侦听器的Node/Deno奇偶校验是后续操作;相同的 WebStandardStreamableHTTPServerTransport 跨运行时工作,只有 Bun.serve(...) 今天的切入点是Bun特有的。
证明:合规套件位于 JsServerConformanceTest.scala 站起来a JsMcpServer 进程中,并通过官方TS SDK客户端驱动每个MCP操作 InMemoryTransport; JsServerHttpTest.scala 验证Bun HTTP路由; ConformanceTest.scala 针对JVM服务器运行JS客户端以实现跨后端奇偶校验。
在Bun上跑步
//> using scala 3.8.3
//> using dep com.tjclp::fast-mcp-scala_sjs1:0.3.2
import com.tjclp.fastmcp.{*, given}
object HelloBun extends McpServerApp[Stdio, HelloBun.type]:
@Tool(name = Some("add"), description = Some("Add two numbers"), readOnlyHint = Some(true))
def add(@Param("First operand") a: Int, @Param("Second operand") b: Int): Int = a + b与JVM形状相同—— McpServerApp trait选择了Scala.js McpServerCoreFactory 给定并构建一个 JsMcpServer 引擎盖下。对于Scala.js上的类型化合约, McpTool[...] 现在也自动生成输入模式;导入 sttp.tapir.generic.auto.* 在调用站点上,就像在JVM上一样。
与链接 ./mill fast-mcp-scala.js.fastLinkJS那么 bun run out/fast-mcp-scala/js/fastLinkJS.dest/main.js。参见 HelloWorldJs.scala 和 HttpServerJs.scala 用于可运行的参考。
规格覆盖范围
快速mcp-scala实现了 MCP规范:
| 能力 | 状态 |
|---|---|
| 工具(列表、调用)+工具注释/提示 | ✅ |
| 静态资源和资源模板 | ✅ |
| 论点提示 | ✅ |
McpContext (客户信息、能力) | ✅ |
| 标准运输 | ✅ |
| 可流式HTTP传输(会话+SSE) | ✅ |
| 无状态HTTP传输 | ✅ |
| 进度通知 | ❌ (尚未) |
| 采样 | ❌ (尚未) |
| 激励 | ❌ (尚未) |
| 完成 | ❌ (尚未) |
| 资源订阅 | ❌ (尚未) |
| 日志级别控制 | ❌ (尚未) |
看 更新日志 用于逐个发布更改。
运行示例
Java虚拟机 — fast-mcp-scala/jvm/src/com/tjclp/fastmcp/examples/:
| 示例 | 演示 |
|---|---|
HelloWorld.scala | 最小可行服务器——一个工具,stdio |
AnnotatedServer.scala | 旗舰注释路径——工具、提示、, @Param 功能、资源、提示 |
ContractServer.scala | 将合同打印为一流价值;跨平台故事 |
TaskManagerServer.scala | 真实的域服务器——自定义Jackson转换器,CRUD风格界面上的提示 |
ContextEchoServer.scala | McpContext 工具处理程序内部的自省 |
HttpServer.scala | 带有curl配方的HTTP传输(可流式默认,通过标志无状态) |
./mill fast-mcp-scala.jvm.runMain com.tjclp.fastmcp.examples.HelloWorld
# or, via scala-cli:
scala-cli scripts/quickstart.scScala.js/Bun — fast-mcp-scala/js/src/com/tjclp/fastmcp/examples/:
| 示例 | 演示 |
|---|---|
HelloWorldJs.scala | Bun上的最小可行服务器——一个工具,stdio |
HttpServerJs.scala | Bun上的流式HTTP传输——有状态会话或无状态会话 |
./mill fast-mcp-scala.js.fastLinkJS
bun run out/fast-mcp-scala/js/fastLinkJS.dest/main.jsClaude桌面集成
添加 claude_desktop_config.json:
{
"mcpServers": {
"fast-mcp-scala-example": {
"command": "scala-cli",
"args": [
"-e",
"//> using dep com.tjclp::fast-mcp-scala:0.3.2",
"--main-class",
"com.tjclp.fastmcp.examples.AnnotatedServer"
]
}
}
}快速mcp-scala示例服务器仅用于演示目的——它们不做任何有用的事情,但它们可以很容易地看到mcp的运行。
有关建筑细节,请参见 docs/architecture.md.
许可证
______________________________________________________________________
在当地发展
构建命令(Mill)
./mill fast-mcp-scala.compile # Compile JVM + Scala.js
./mill fast-mcp-scala.test # All tests (JVM + Bun conformance)
./mill fast-mcp-scala.checkFormat # Scalafmt check (all sources)
./mill fast-mcp-scala.reformat # Auto-format (all sources)
./mill fast-mcp-scala.jvm.test # JVM tests only
./mill fast-mcp-scala.js.test.bunTest # Scala.js conformance tests only
./mill fast-mcp-scala.jvm.publishLocal # Publish JVM artifact to ~/.ivy2/local消费本地建筑
之后 publishLocal:
libraryDependencies += "com.tjclp" %% "fast-mcp-scala" % "0.3.3-SNAPSHOT"或者使用Mill:
def ivyDeps = Agg(
ivy"com.tjclp::fast-mcp-scala:0.3.3-SNAPSHOT"
)或点 scala-cli 直接在构建的JAR中:
//> using scala 3.8.3
//> using jar "/absolute/path/to/out/fast-mcp-scala/jvm/jar.dest/out.jar"
//> using options "-Xcheck-macros" "-experimental"
