脚本化API运行时

将OpenAPI规范和MCP服务器转换为可脚本化的运行时环境,通过单一往返调用替代多次API请求,优化AI代理资源消耗。

  • by alennartz
  • Rust
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 alennartz/code-mcp

使用说明

工具脚本

将OpenAPI规范和MCP服务器转化为可脚本化的运行时环境。一次往返,而不是多次往返。

问题

在复杂的API上使用MCP工具的AI代理浪费了资源。每个API调用都成为一个单独的工具调用,即使在不需要判断的情况下,LLM round-tips也会处理所有中间状态。LLM变成了一个昂贵的数据穿梭机。

解决方案

toolscript为LLM提供了 Luau 使用强类型SDK编写运行时脚本。SDK可以从OpenAPI规范自动生成,从上游MCP服务器填充,或两者兼而有之。LLM编写一个脚本,将多个调用链接起来,发送执行,并返回结果。一次往返,而不是多次往返。

快速开始

cargo install --path .

指向OpenAPI规范并提供您的API密钥:

export MY_TOKEN=your-token-here
toolscript run petstore=https://petstore3.swagger.io/api/v3/openapi.json \
  --auth petstore:MY_TOKEN

或者使用配置文件(toolscript.toml):

[apis.petstore]
spec = "https://petstore3.swagger.io/api/v3/openapi.json"
auth = "your-token-here"
toolscript run

或者连接到上游MCP服务器,而不是(或与OpenAPI规范一起):

# MCP-only
toolscript run --mcp filesystem='npx -y @modelcontextprotocol/server-filesystem /tmp'

# Mixed: OpenAPI + MCP
toolscript run petstore=petstore.yaml \
  --auth petstore:MY_TOKEN \
  --mcp filesystem='npx -y @modelcontextprotocol/server-filesystem /tmp'

将服务器添加到MCP客户端配置中:

{
  "mcpServers": {
    "petstore": {
      "command": "toolscript",
      "args": ["run", "petstore=https://petstore3.swagger.io/api/v3/openapi.json", "--auth", "petstore:PETSTORE_TOKEN"],
      "env": {
        "PETSTORE_TOKEN": "your-token-here"
      }
    }
  }
}

运作原理

  1. 代理连接到MCP服务器。
  2. 它使用文档工具探索SDK(list_apis, list_functions, get_function_docs, search_docs)或通过浏览资源(sdk://petstore/overview, sdk://petstore/functions等等)。
  3. 它编写了一个链接SDK调用的Luau脚本。
  4. 它将脚本发送到 execute_script.
  5. 它在一个响应中返回结果并捕获日志。

LLM可能编写的示例脚本:

-- OpenAPI calls
local pets = sdk.list_pets({ limit = 5 })
local first = pets[1]
local details = sdk.get_pet({ pet_id = first.id })
return { pet = details, total = #pets }

脚本还可以调用同一命名空间中的上游MCP工具:

-- MCP tool call (filesystem server)
local content = sdk.filesystem.read_file({ path = "/tmp/data.txt" })
return json.decode(content)

OpenAPI函数和MCP工具在 sdk.* 并且可以在单个脚本中自由混合。响应包括JSON格式的返回值,任何 print() 以日志形式捕获的输出,以及 files_touched 数组汇总通过沙盒写入或删除的文件 io 图书馆。

CLI参考

toolscript run

一步生成和服务。这是最常见的子命令。

toolscript run [SPECS]... [OPTIONS]
标志 默认值 描述
--config -- TOML配置文件的路径
--auth -- API授权: name:ENV_VAR 或 ENV_VAR
--mcp -- 上游MCP服务器: name=command 或 name=url
--transport stdio 运输类型(stdio, sse)
--port 8080 HTTP/SSE传输端口
--timeout 30 脚本执行超时(秒)
--memory-limit 64 Luau VM内存限制(MB)
--max-api-calls 100 每个脚本的最大上行调用数(API+MCP)
--io-dir -- 用于沙盒文件访问的I/O目录
--auth-authority -- OAuth颁发者URL(启用JWT身份验证)
--auth-audience -- 预计JWT观众
--auth-jwks-uri -- 显式JWKS URI覆盖

规格在以下情况下是可选的 --mcp 或 [mcp_servers] config至少提供一个源。如果没有规格,就没有 --mcp,而且没有 --config 提供, toolscript run 寻找 toolscript.toml 在当前目录中。

toolscript generate

仅用于代码生成。在不启动服务器的情况下生成清单和SDK注释。

toolscript generate <SPECS>... [-o <DIR>] [--config <FILE>]

输出目录默认为 ./output。生成 manifest.json 和 sdk/*.luau.使用 --config 从TOML配置文件而不是位置参数加载规范。

toolscript serve

从预先生成的输出目录启动MCP服务器。

toolscript serve <DIR> [OPTIONS]

接受与相同的选项 run (--auth, --mcp, --transport, --port, --timeout, --memory-limit, --max-api-calls, --io-dir, --auth-authority, --auth-audience, --auth-jwks-uri).

认证

有两个单独的身份验证层。

上游API证书

这些是toolscript用来调用SDK后面的API的凭据。

命令行界面 --auth 旗帜 (快速启动):

# Named: --auth name:ENV_VAR
toolscript run petstore=spec.yaml --auth petstore:MY_TOKEN

# Unnamed (single-spec only): --auth ENV_VAR
toolscript run spec.yaml --auth MY_TOKEN

该工具在启动时读取环境变量的值。秘密永远不会出现在命令本身。

配置文件 (toolscript.toml):

[apis.petstore]
spec = "https://petstore.example.com/spec.json"
auth = "sk-my-token"

[apis.stripe]
spec = "./stripe.yaml"
auth = "sk_live_abc123"

[apis.legacy]
spec = "./legacy.yaml"

[apis.legacy.auth]
type = "basic"
username = "admin"
password = "secret"

使用 auth_env 而不是 auth 引用环境变量:

[apis.stripe]
spec = "./stripe.yaml"
auth_env = "STRIPE_KEY"

使用配置文件运行:

toolscript run --config toolscript.toml
# Or just have toolscript.toml in the current directory:
toolscript run

根据请求,通过 _meta.auth (对于托管模式,覆盖全部):

{
  "method": "tools/call",
  "params": {
    "name": "execute_script",
    "arguments": { "script": "return sdk.list_pets()" },
    "_meta": {
      "auth": {
        "petstore": { "type": "bearer", "token": "sk-runtime-token" }
      }
    }
  }
}

解析顺序 (首场比赛获胜):

  1. 命令行界面 --auth 旗帜
  2. 配置文件 auth / auth_env
  3. 根据请求 _meta.auth

MCP层身份验证

这控制了谁可以连接到工具脚本服务器本身。它仅适用于使用HTTP/SSE传输时。

  • 使用OIDC发现进行JWT验证
  • 启用 --auth-authority 和 --auth-audience
  • 可选地用以下命令覆盖JWKS端点 --auth-jwks-uri
  • 出版 /.well-known/oauth-protected-resource 用于客户端发现

对于本地stdio的使用,不需要这一层——MCP客户端和服务器共享相同的信任边界。

冻结参数

冻结参数是在请求时注入API调用的服务器端固定值。它们完全隐藏在LLM之外——从工具模式、文档和搜索结果中剥离出来。使用它们来硬编码值,如API版本、租户ID或特定于环境的设置。

在中配置冻结参数 toolscript.toml 在两个层面上:

# Global — applies to every API
[frozen_params]
api_version = "v2"

# Per-API — applies only to this API's operations
[apis.petstore]
spec = "petstore.yaml"
[apis.petstore.frozen_params]
tenant_id = "abc-123"

优先: 当两个参数名称相同时,Per-API值将覆盖全局值。不匹配的参数名称(操作中不存在的参数)将被自动忽略。

它是如何工作的: 在代码生成过程中,冻结的参数保留其完整的元数据(名称、位置、类型),但用固定值标记。在运行时,服务器将配置的值注入到正确的位置(路径、查询字符串或标头),而LLM不需要知道这些位置。

上游MCP服务器

toolscript可以连接到外部MCP服务器,并将它们的工具作为可调用的Luau函数与OpenAPI生成的函数一起公开。来自上游MCP服务器的工具出现在 sdk.<server>.<tool>() 命名空间。

命令行界面 --mcp 旗帜

# Stdio: name=command with args
toolscript run --mcp filesystem='npx -y @modelcontextprotocol/server-filesystem /tmp'

# HTTP: name=url (uses streamable-http transport)
toolscript run --mcp remote=https://mcp.example.com/mcp

# Multiple servers
toolscript run --mcp filesystem='npx -y @modelcontextprotocol/server-filesystem /tmp' \
               --mcp db='npx -y @modelcontextprotocol/server-sqlite db.sqlite'

如果该值以开头 http:// 或 https://,它被视为一个URL。否则,它被拆分为空格:第一个标记是命令,其余的是参数。

配置文件

# Stdio-based (spawns a child process)
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
env = { HOME = "/tmp" }

# HTTP-based (streamable-http transport)
[mcp_servers.remote]
url = "https://mcp.example.com/mcp"

每个条目必须恰好有一个 command 或 urlThe args 和 env 字段仅在以下情况下有效 command基于URL的服务器使用可流式传输的http。不支持传统SSE传输。

命令行界面 --mcp 标志与配置文件条目合并。如果两者定义了相同的服务器名称,则CLI标志获胜。

MCP工具的外观

MCP工具已完全集成到发现工具和资源中。 list_apis 包括MCP服务器和OpenAPI API。 list_functions 返回MCP工具和OpenAPI函数,可按服务器名称过滤。 get_function_docs 返回任何MCP工具的完整Luau类型注释。 search_docs 在MCP工具名称、描述和参数之间进行搜索。

在Luau脚本中,MCP工具在服务器名称下以命名空间排列:

-- Call an MCP tool
local result = sdk.filesystem.read_file({ path = "/tmp/data.txt" })

-- MCP tool results: text content is returned as a string,
-- structured content as a table. Parse JSON yourself if needed:
local data = json.decode(result)

执行限制

标志 默认值 控件
--timeout 30s 每个脚本执行的挂钟截止日期
--memory-limit 64 MB 最大Luau VM内存分配
--max-api-calls 100 每个脚本的最大上行调用数(API+MCP)

OpenAPI HTTP请求和MCP工具调用都计入相同的限制。CPU受到挂钟超时的间接限制。没有单独的指令计数限制。

MCP工具和资源

工具

工具 说明
list_apis 列出加载的API和MCP服务器,包括名称、描述和计数
list_functions 列出SDK功能和MCP工具,可通过API/服务器或标签进行筛选
get_function_docs 函数或MCP工具的完整Luau类型注释,带有引用的模式
search_docs 在所有SDK和MCP工具文档中进行全文搜索
execute_script 对SDK执行Luau脚本

资源

可浏览的SDK文档,可通过以下方式访问 resources/read:

URI模式 内容
sdk://{api}/overview API或MCP服务器概述
sdk://{api}/functions 所有功能/工具签名
sdk://{api}/schemas 所有类型定义(仅限OpenAPI)
sdk://{api}/functions/{name} 单个函数文档(仅限OpenAPI)
sdk://{api}/schemas/{name} 单个模式文档(仅限OpenAPI)

这 overview 和 functions 为OpenAPI API和上游MCP服务器生成资源。

沙盒安全

脚本在沙盒Luau VM中执行。以下是可用和不可用的内容。

允许:

  • 标准库: string, table, math
  • os.clock() (仅限挂钟计时)
  • os.remove() (删除I/O目录中的文件)
  • print() (捕获到日志中,不写入stdout)
  • json.encode() / json.decode()
  • sdk.* 功能(来自OpenAPI规范和上游MCP服务器)
  • io.open(), io.lines(), io.list(), io.type() (沙盒文件I/O,见下文)

有条件可用--沙盒 io:

这 io 库是Lua标准的沙盒子集 io。所有路径都是相对于单个I/O目录解析的(默认 ./toolscript-files,用以下命令覆盖 --io-dir).此目录外的路径遍历被拒绝。在stdio模式下, io 默认情况下启用。在托管(HTTP/SSE)模式下,除非通过以下方式明确启用,否则它将被禁用 --io-dir 或 [io] 配置部分。

此 路 不通:

  • os.execute (外壳访问)
  • loadfile, dofile, require (模块加载)
  • debug 图书馆
  • string.dump (字节码访问)
  • load (动态代码加载)
  • 原始网络访问
  • 非沙盒文件系统访问

执行机制:

  • Luau原生沙盒模式(只读全局变量,按脚本隔离环境)
  • 可配置的内存限制
  • 通过Luau中断回调实现挂钟超时
  • 每次执行API调用计数器
  • 每次执行新的VM(脚本之间没有状态泄漏)
  • 凭据从未暴露给Luau——注入服务器端

关于托管的说明。 如果您通过HTTP为多个用户部署toolscript,则您将计算作为代码沙箱提供。沙盒限制了滥用表面,但您应该在适当的资源约束和网络策略之后进行部署。对于大多数用例,使用自己的凭据在stdio上本地运行是最简单、最安全的选择。

码头工人

构建并运行:

docker build -t toolscript .
docker run toolscript https://api.example.com/openapi.json

对于HTTP传输:

docker run -p 8080:8080 toolscript \
  https://api.example.com/openapi.json \
  --transport sse --port 8080

Docker和MCP服务器

Docker镜像构建自 scratch --它只包含静态链接的二进制和CA证书。这意味着它是为 基于HTTP的托管部署 其中下游传输(客户端如何连接到工具脚本)和任何上游MCP服务器都使用HTTP。

HTTP上游MCP服务器开箱即用:

docker run -p 8080:8080 toolscript \
  --mcp remote=https://mcp.example.com/mcp \
  --transport sse --port 8080

Stdio上游MCP服务器将无法工作 在默认图像中。Stdio模式生成子进程(例如。 npx, python),并且这些运行时间不存在于划痕图像中。这是设计上的——stdio MCP服务器适用于单用户本地场景,其中工具脚本直接在主机上运行,而不是用于托管的多用户部署。

如果需要容器中的stdio MCP服务器,请使用所需的运行时扩展映像:

FROM node:20-slim
COPY --from=toolscript:latest /toolscript /toolscript
RUN npm install -g @modelcontextprotocol/server-filesystem
ENTRYPOINT ["/toolscript", "run"]

在实践中,大多数容器化部署都应该使用基于HTTP的上游MCP服务器。如果上游服务器只支持stdio,可以考虑在HTTP适配器后面运行它,或者将其作为暴露HTTP端点的sidecar运行。

从源头构建

git clone https://github.com/alenna/toolscript.git
cd toolscript
cargo build --release
cargo test

需要Rust 1.85+(使用2024版)。

许可证

麻省理工学院