bldr

bldr

A lightweight build daemon that exposes project build flows as MCP tools, enabling AI assistants to drive builds, check status, and react to results.

Category
访问服务器

README

bldr

A lightweight, language-agnostic build daemon that exposes your project's build flows as MCP (Model Context Protocol) tools, letting an AI assistant like Claude drive builds, check status, and react to results without any project-specific plumbing.

Homepage: bldr-ecbf6d.gitlab.io (Next.js site in site/, built by bldr itself via bldr --project site/bldr.toml --flow build and published to GitLab Pages by .gitlab-ci.yml)


What it does

  • Runs as a single long-lived process managing one or more projects.
  • Each project is described by a bldr.toml that declares stages, flows, and optional capability plugins.
  • Stages are shell commands; flows are ordered lists of stages.
  • The daemon exposes FastMCP tools so an MCP client can trigger and monitor builds, manage worktrees, and invoke plugin actions.
  • State is persisted in SQLite (XDG data dir) so build history survives restarts. Any record that was "running" at restart is marked "interrupted" -- the daemon never reports stale in-progress state.
  • Per-project async locks mean projects build concurrently without stepping on each other.
  • Projects can be added and removed at runtime via add_project / remove_project.

Architecture

                       one daemon, many projects
  MCP client (Claude) --> FastMCP tools (project-routed) --> ProjectRegistry
                                     |                            |
                               /status endpoint            per-project:
                               (GET, JSON)                 ProjectState + Lock
                                                           pipeline (flow runner)
                                                           runner (async shell)
                                                           plugins (Capabilities)
                                                                  |
                                                           SQLite (XDG data dir)

Concepts

Stages

A stage is a named shell command declared in [stages.<name>]:

Field Default Description
cmd (required) Shell command to run. Supports {{message}}, {{branch}}, {{project_root}} templates.
cwd project root Override working directory for this stage only.
interruptible true If false, a new build request queues behind this stage rather than cancelling it.
timeout none Max seconds before the stage is killed.

Flows

A flow is an ordered list of stage names. When request_build is called:

  • If nothing is running, the flow starts immediately.
  • If the current stage is interruptible, the running flow is cancelled and the new flow starts.
  • If the current stage is non-interruptible, the new request is queued and starts as soon as that stage finishes.

Plugins (Capabilities)

A plugin is a Python Capability subclass registered under [plugins.<key>] in bldr.toml. Plugins can:

  • Start and stop background services (start() / stop()).
  • Contribute named stage handlers used inside flows.
  • Register MCP tools available to the AI client.

Worktrees

Worktrees are parallel isolated builds of the same project, backed by Git worktrees. Each worktree appears in the registry as <project>::<name> and gets its own plugin instances (with ephemeral ports to avoid conflicts).


MCP Tools

All project-scoped tools require a project argument (the value of name in [project]). There is no single-project mode; projects are always addressed by name.

Core tools

Tool Arguments Description
list_projects -- List all registered projects with their flows and status.
request_build project, flow, message="" Start, queue, or restart a flow. Returns started, queued, or restarted.
get_status project Return current status, running stage, recent log lines, and build history.
add_project config_path Register a new project from a bldr.toml path. Starts its plugins.
remove_project project Unregister a project, cancel any running flow, stop its plugins.

Worktree tools

Tool Arguments Description
create_worktree project, name, branch="" Create a Git worktree of project, register it as project::name.
list_worktrees project List all registered worktrees for a project.
build_worktree project, name, flow, message="" Run a flow on a worktree (project::name).
remove_worktree project, name Cancel the worktree's flow, stop its plugins, and remove the Git worktree.

Plugin tools

These tools are registered by their respective plugins and are only available when the project declares that plugin in its bldr.toml.

Tool Plugin Arguments Description
list_artifacts artifact_http project List files currently in the artifacts directory.
serve web_serve project (Re)start the web server and return its URLs.
screenshot_build screenshot project, url, wait_seconds=3.0, width=0, height=0 Capture a headless screenshot and save it to out_dir.
compare_screenshot screenshot project, url, baseline_name, save_baseline=False, diff_threshold=None, wait_seconds=3.0, width=0, height=0 Compare a live screenshot to a saved baseline; returns match/mismatch and diff percent.
request_demo_deploy demo_deploy project, push=False Copy the build dir into a target git repo, patch index.html, commit, and optionally push.

Configuration

Each project requires a bldr.toml. All paths are resolved relative to the config file unless absolute.

[project]
name = "my-app"
# root defaults to the directory containing bldr.toml.
root = "."

# Flows are ordered lists of stage names.
[flows]
build   = ["clean", "compile", "package"]
test    = ["clean", "compile", "test_suite"]
release = ["clean", "compile", "package", "artifact_publish"]

# Each stage must have a cmd. Other fields are optional.
[stages.clean]
cmd           = "rm -rf build dist"
interruptible = true        # default

[stages.compile]
cmd           = "make -j4"
interruptible = false       # queues new requests until this stage finishes
timeout       = 120         # seconds; omit to disable

[stages.package]
cmd           = "make dist"

[stages.test_suite]
cmd           = "pytest -q"
timeout       = 300
# cwd = "../other-dir"   # per-stage working directory override

# -- Plugins --

# artifact_http: copy build outputs to a served directory.
# Adds an "artifact_publish" stage handler and a "list_artifacts" MCP tool.
[plugins.artifact_http]
dir       = "artifacts"              # relative to project root
port      = 9099                     # 0 = ephemeral port
bind      = ["127.0.0.1"]
keep      = 10                       # prune oldest beyond this count
artifacts = ["build/my-app.tar.gz", "build/index.html"]

# web_serve: serve a build directory with Cache-Control: no-store.
# Adds a "serve" MCP tool.
[plugins.web_serve]
build_dir = "build"
port      = 8100
bind      = ["127.0.0.1"]

# screenshot: headless-browser screenshots via Playwright.
# Adds "screenshot_build" and "compare_screenshot" MCP tools.
[plugins.screenshot]
viewport       = [412, 915]   # [width, height] in pixels (default)
out_dir        = "screenshots"
diff_threshold = 0.01         # fraction of changed pixels to count as mismatch
noise          = 30           # per-channel delta below which a pixel is "same"

# demo_deploy: copy a build directory into a target git repo and commit.
# Adds a "demo_deploy" stage handler and a "request_demo_deploy" MCP tool.
[plugins.demo_deploy]
build_dir         = "build"
target_repo       = "/path/to/pages-repo"
target_subdir     = "."           # subdirectory inside target_repo
base_href         = "/my-app/"    # patches <base href> in index.html
page_title        = "My App"      # patches <title>
page_description  = "My demo"     # patches <meta name="description">
commit_message    = "Deploy demo via bldr"
branch            = "main"

Key rules:

  • Every stage name referenced in a flow must have a [stages.<name>] entry OR be provided by a plugin's stage_handlers().
  • An unknown or broken plugin is logged and skipped; it does not crash the daemon.

Built-in Plugins

artifact_http

Copies matched glob patterns from the project root into a served directory, renames each by the name template, prunes the oldest files beyond keep, and serves the directory over plain HTTP (with no index.html, the default Python http.server directory listing). Bind it to a LAN/VPN address and any machine can browse the directory and download artifacts.

Option Default Description
dir "artifacts" Directory to publish into (relative to project root).
port 9099 TCP port; 0 picks a free ephemeral port.
bind ["127.0.0.1"] Addresses to bind (a down interface is skipped).
keep 10 Max files to retain (oldest pruned); 0 keeps everything.
name "{name}" Filename template for published artifacts (see below).
artifacts [] Glob patterns (relative to project root) to copy on artifact_publish.

The name template builds each published filename from these placeholders: {name} (original basename), {stem}, {ext} (incl. the dot), {project}, {branch} (sanitized), {sha} (short git hash), {ts} (YYYYMMDD-HHMMSS). The default "{name}" keeps the basename (each build overwrites the last). A template like "{project}-{branch}-{ts}-{sha}{ext}" makes every build a distinct file, so the whole build history accumulates and stays browsable over HTTP (bounded by keep). Different artifact kinds (e.g. .apk and .aab) are kept apart by {ext}. The git branch/sha are read from the project root, so a worktree publishes under its own branch.

Stage handler: artifact_publish -- copies + renames matching files, logs their URLs. MCP tool: list_artifacts(project) -- returns sorted filenames in the directory.

web_serve

Serves a build directory with Cache-Control: no-store so browsers always fetch the latest build. Restarts the server each time the serve tool is called.

Option Default Description
build_dir "build" Directory to serve (relative to project root).
port 8100 TCP port.
bind ["127.0.0.1"] List of addresses to bind.

MCP tool: serve(project) -- restarts the server and returns its URL(s).

screenshot

Uses Playwright (Chromium, headless) to capture screenshots and run pixel-diff regression checks. Requires playwright and Pillow to be installed.

Option Default Description
viewport [412, 915] [width, height] in pixels.
out_dir "screenshots" Directory for captured PNGs. Baselines go in out_dir/baselines/.
diff_threshold 0.01 Fraction of pixels that may differ before a comparison is "mismatch".
noise 30 Per-channel delta (0-255) below which a pixel is considered unchanged.

MCP tools:

  • screenshot_build(project, url, wait_seconds, width, height) -- captures and saves a PNG.
  • compare_screenshot(project, url, baseline_name, save_baseline, diff_threshold, wait_seconds, width, height) -- compares to a saved baseline; saves a diff image on mismatch.

demo_deploy

Copies a build directory into a separate git repository (e.g. a GitLab Pages repo), optionally patches index.html (base href, title, description), commits the change, and optionally pushes.

Option Default Description
build_dir "build" Source directory (relative to project root).
target_repo "" Absolute path to the target git repository.
target_subdir "." Subdirectory inside target_repo to deploy into.
base_href none Replaces <base href="..."> in index.html.
page_title none Replaces <title> in index.html.
page_description none Replaces <meta name="description"> in index.html.
commit_message "Deploy demo via bldr" Git commit message.
branch "main" Branch to push to when push=True.

Stage handler: demo_deploy -- deploys without pushing (commit only). MCP tool: request_demo_deploy(project, push=False) -- deploy on demand; set push=True to also push to the remote.

Security note: artifact_http and web_serve are unauthenticated static file servers. They default to loopback (127.0.0.1). Setting a non-loopback bind address (e.g. 0.0.0.0) exposes served files to anyone on the network -- use that option only on trusted networks.


Plugin authoring

Subclass Capability, implement the hooks you need, then call register_capability with an optional module-level tool registrar.

from bldr.plugins.base import Capability, register_capability


class MyPlugin(Capability):
    name = "my_plugin"

    def __init__(self, project_config, options: dict):
        super().__init__(project_config, options)
        # project_config.root is the absolute path to the project directory.
        # options is the dict from [plugins.my_plugin] in bldr.toml.
        self.port = int(options.get("port", 8080))

    async def start(self) -> None:
        # Start a background service (e.g. an HTTP server). Optional.
        pass

    async def stop(self) -> None:
        # Tear down the background service. Optional.
        pass

    def stage_handlers(self) -> dict:
        # Return a mapping of stage name -> async callable.
        # The pipeline calls: await handler(registered_project, build_record)
        async def my_stage(rp, rec) -> None:
            rp.state.log("running my_stage")
        return {"my_stage": my_stage}


def register_tools(mcp, lookup):
    # MCP tools are registered once per plugin TYPE, not per project instance.
    # Use lookup(project, "my_plugin") to resolve the per-project capability.
    @mcp.tool()
    async def my_tool(project: str) -> dict:
        cap = lookup(project, "my_plugin")
        if cap is None:
            return {"error": f"project '{project}' has no my_plugin"}
        return {"port": cap.port}


register_capability("my_plugin", MyPlugin, register_tools)

The register_tools(mcp, lookup) function is called once at daemon startup. The lookup(project, cap_name) helper resolves the named capability for any registered project. Tool names must be globally unique across all plugins.


Running

# Start with one project pre-loaded
bldr --project path/to/bldr.toml

# Start with multiple projects
bldr --project proj-a/bldr.toml --project proj-b/bldr.toml

# Start empty; add projects via the add_project MCP tool at runtime
bldr

# Custom host/port (default: 127.0.0.1:7900)
bldr --host 0.0.0.0 --port 8000 --project bldr.toml

The --project flag is repeatable and optional. Projects can also be registered and removed at runtime via the add_project and remove_project MCP tools.

TUI monitor

bldr-tui is a live, interactive monitor (Textual). It polls the daemon's /status endpoint and shows a multi-pane view for the selected project:

  • a status bar (stage icon/color, current message, elapsed time, pending),
  • a streaming build log and web-server log,
  • a projects/worktrees pane (status, port, branch, commit; worktrees nested under their parent),
  • a build-history pane.
bldr-tui                          # connects to http://127.0.0.1:7900
bldr-tui --url http://host:8000   # custom daemon address

Keys: j/k select project/worktree, b build (prompts for flow), w new worktree, d remove worktree, r refresh, c clear, n toggle desktop notifications, a toggle browser auto-reload (reloads pages of a Chrome started with --remote-debugging-port=9222 when a build finishes), R reload now, q quit. Actions are performed over MCP. Requires textual (a dependency); the view refreshes every 1.5 s.

CLI client (bldr-cli)

bldr-cli is a command-line client for the daemon's MCP tools: each tool is a subcommand, positional args fill the tool's parameters in schema order, key=value sets a named one, and values are JSON-coerced (numbers, booleans, null, arrays, objects). Output is pretty-printed JSON; pass -j/--json for compact single-line output.

bldr-cli --help                         # usage + all tools + aliases
bldr-cli tools                          # list tools and their parameters
bldr-cli ls                             # list_projects
bldr-cli st kairos                      # get_status kairos (pretty)
bldr-cli build kairos web               # request_build project=kairos flow=web
bldr-cli request_demo_deploy kairos push=true
bldr-cli screenshot_build kairos http://127.0.0.1:8010
bldr-cli -j ls                          # compact JSON (scripting)

Aliases: ls=list_projects, st/status=get_status, build=request_build, add=add_project, rm=remove_project. The daemon URL defaults to $BLDR_URL or http://127.0.0.1:7900/mcp (override with --url).

Status endpoint

GET /status on the daemon returns a JSON snapshot of all projects -- the same data the TUI polls. Useful for scripting or health checks.


Bundled example

example-project/ exercises the full pipeline without any external toolchain:

example-project/
  bldr.toml        # build + release flows, artifact_http plugin
  build.sh         # copies site/ into build/
  site/
    index.html

Flows:

  • build -- runs clean then build_site (produces build/index.html).
  • release -- runs clean, build_site, then artifact_publish (copies the file into artifacts/ and serves it over HTTP on an ephemeral port).
cd example-project
bldr --project bldr.toml
# then call request_build("example-site", "release", "manual test") via MCP

Testing

pip install -e ".[dev]"
pytest

70 tests covering the pipeline, registry, plugins, worktrees, CLI, TUI, and end-to-end integration.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选