> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bricks.tools/llms.txt
> Use this file to discover all available pages before exploring further.

# 本地函式

> 將 BRICKS Buttress 伺服器上的 .ts / .js 檔案公開為 MCP 工具與 HTTP 端點

本地函式是你放進 [BRICKS Buttress](/zh-Hant/buttress) 伺服器某個目錄裡的 `.ts` / `.js` 檔案。每個檔案同時成為一個 MCP 工具**與**一個 HTTP 端點，讓 agent（或任何 HTTP 用戶端）能在伺服器上執行工作：呼叫 `ffmpeg`、使用該伺服器自己的 LLM、語音轉文字與文字轉語音 generator，或連線到內部服務。你只需要寫一個檔案，不需要另外架一套服務。

<Warning>
  本地函式屬於實驗性功能。端點、函式檔案契約、`context` API、自訂 `_auth` 以及 `[functions]` 設定鍵都可能在版本之間變動，且不保證有淘汰緩衝期。伺服器啟動時會顯示這項提醒。若你要以此為基礎開發，請鎖定 `bricks-buttress` 版本，並在升級後重新閱讀本頁。
</Warning>

本地函式與 Buttress 的其他部分不同。其他地方的 Buttress 是把 [BRICKS Foundation](/zh-Hant/foundation) 裝置原本要自己跑的 generator 卸載到伺服器；本地函式則是在**伺服器上**執行，由維運者撰寫，並由 agent 與 HTTP 用戶端呼叫，而不是由應用程式執行階段呼叫。

## 啟用

本地函式預設關閉。在伺服器 TOML 中加入 `[functions]` 區段即可啟用：

```toml theme={null}
[functions]
enabled = true
dir = "./functions"              # 相對路徑以此設定檔為基準
# default_timeout = "5m"         # 每次呼叫的期限；函式可自行覆寫
# hot_reload = false             # 監看目錄，存檔即重新載入
# allow_unauthenticated = false  # 參見下方「安全性」
# cors_allowed_origins = ["http://localhost:3000"]

[functions.config]               # 選填；會以 context.config 傳給函式
# api_base = "https://example.internal"
```

| 鍵                       | 型別                | 預設值     | 說明                                                |
| ----------------------- | ----------------- | ------- | ------------------------------------------------- |
| `enabled`               | boolean           | `false` | 啟用 `/functions` 介面                                |
| `dir`                   | string            | —       | 函式目錄。必填；相對路徑以設定檔為基準，`~` 會展開為家目錄                   |
| `default_timeout`       | number 或 string   | `"5m"`  | 每次呼叫的期限（毫秒），或時間字串。函式的 `meta.timeout` 優先           |
| `hot_reload`            | boolean           | `false` | 除了每次呼叫前的檢查外，額外監看目錄並在存檔時重新載入                       |
| `allow_unauthenticated` | boolean           | `false` | 允許**未繫結**的伺服器接受呼叫。參見[安全性](#安全性)                   |
| `cors_allowed_origins`  | string\[] 或 `"*"` | —       | 允許呼叫函式的瀏覽器來源。留空表示不允許任何瀏覽器；只有 `"*"` 能放行 no-CORS 載入 |
| `[functions.config]`    | table             | `{}`    | 自由格式的值，會以 `context.config` 傳給每個函式                 |

前三個鍵有對應的環境變數：`ENABLE_FUNCTIONS_ENDPOINT=1`、`BUTTRESS_FUNCTIONS_DIR=<dir>` 與 `BUTTRESS_FUNCTIONS_HOT_RELOAD=1`。`BUTTRESS_FUNCTIONS_ALLOW_UNAUTHENTICATED=1` 則對應 `allow_unauthenticated`。

<Note>
  若設定了 `enabled = true` 但未指定目錄，伺服器會記錄警告並維持關閉，而不會自行猜測路徑。
</Note>

### 伺服器會產生的檔案

每次啟動時，伺服器會在目錄不存在時建立它，並寫入 `buttress-functions.d.ts`，其中包含 `ButtressFunctionMeta`、`ButtressFunctionContext` 與 `ButtressAuth*` 等型別。這個檔案每次啟動都會重新產生，因此永遠與執行中的伺服器一致。當本頁與你的伺服器不一致時，以該檔案為準。

若目錄中尚無任何函式，伺服器還會一併產生 `tsconfig.json` 與附註解的 `_example.ts`。這兩個檔案一旦你編輯過就不會再被覆寫。

## 撰寫函式

一個檔案就是一個函式，而且**檔名就是工具名稱** — `video-duration.ts` 會成為 `video-duration` 工具。

```ts theme={null}
export const meta: ButtressFunctionMeta = {
  description: 'Transcribe the audio track of a video file',
  parameters: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Path to a video on the server' },
    },
    required: ['path'],
  },
  timeout: '10m',
}

export default async function ({ path }: { path: string }, context: ButtressFunctionContext) {
  const wav = `${context.tempDir}/audio.wav`
  const { code, stderr } = await context.spawn('ffmpeg', ['-i', path, '-ar', '16000', wav])
  if (code !== 0) throw new Error(`ffmpeg failed: ${stderr}`)

  context.emit('progress', { stage: 'transcribing' })
  return context.buttress.transcribe({ filePath: wav })
}
```

規則：

* 預設匯出**必須**是函式，並接收 `(input, context)`。
* `meta` 為選填。`meta.parameters` 是純 JSON Schema，會原封不動交給 MCP 用戶端，因此你只需描述輸入一次，所有介面就會一致。若省略，工具會宣告一個空的 object schema。
* 回傳可序列化為 JSON 的值。若要回傳檔案，請寫入 `context.tempDir` 並回傳 `context.fileUrl(path)`，參見[傳入與傳出檔案](#傳入與傳出檔案)。
* 函式名稱必須以字母或數字開頭，其餘可包含字母、數字、`-` 與 `_`。

探索時會略過以 `_` 開頭的檔案、隱藏檔、`*.d.ts`、`*.test.*`、`*.spec.*` 以及子目錄 — 子目錄是給你匯入的輔助模組使用。`_auth.ts` 是底線規則的唯一例外：它是有效的，而且會改變每個端點的驗證方式，參見[自訂驗證](#自訂驗證)。若同名的 `.ts` 與 `.js` 並存，以 `.ts` 為準。

`mcp`、`files` 與 `upload` 是保留名稱，因為 `/functions/mcp`、`/functions/files/*` 與 `/functions/upload` 都是路由。使用這些名稱的檔案會被略過。

### Context

第二個參數提供函式可以取用的一切：

| 成員                                                                    | 用途                                                                                                                                                                                                                                     |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `spawn(cmd, args?, opts?)`                                            | 執行子處理程序，回傳 `{ code, signal, stdout, stderr, truncated }`。**非零結束碼仍算成功回傳** — 請自行檢查 `code`。只有在處理程序無法啟動時才會 reject。選項：`cwd`、`env`、`input`、`encoding`（`"utf8"` 或 `"buffer"`）、`maxBuffer`（每個串流預設 8 MB）、`onStdout`、`onStderr`                  |
| `buttress.completion({ model?, messages, max_tokens?, onToken?, … })` | 在本伺服器的 LLM generator 上執行 chat completion，回傳 `{ content, reasoning_content?, tool_calls?, usage }`。`model` 必須對應已設定的 `[[generators]]` 項目，省略則使用第一個。`messages` 會套用模型自己的 chat template，除非傳入 `enable_thinking: true`，否則不會輸出思考內容。其餘鍵會原封不動傳給後端 |
| `buttress.embedding({ model?, text, embd_normalize? })`               | 產生文字的嵌入向量，回傳 `{ embedding: number[] }`。僅限 GGML：`model` 必須對應 `[generators.model]` 設定 `embedding = true` 的 `ggml-llm` generator，指向其他 generator 的呼叫會被拒絕。`embd_normalize` 選擇 llama.cpp 的正規化模式（原生預設為 `2`，即 L2）                              |
| `buttress.tokenize({ model?, text, params? })`                        | 以已設定的 GGML 或 MLX LLM 進行 tokenize，回傳 `{ tokens: number[], … }`                                                                                                                                                                          |
| `buttress.detokenize({ model?, tokens })`                             | 以同一個模型把 token id 還原成文字                                                                                                                                                                                                                 |
| `buttress.transcribe({ model?, filePath \| audioData, options? })`    | 在本伺服器的 STT generator 上執行語音轉文字。與 LLM 路徑不同，這裡不會自動替換 — `model` 必須符合已設定的 STT 模型（`repo_id` 或 `repo_id:filename`）                                                                                                                            |
| `buttress.synthesize({ model?, text, options? })`                     | 在本伺服器的 `onnx-tts` 或 `ggml-tts` generator 上執行文字轉語音，回傳 `{ path, sampling_rate, channels }`。WAV 會複製到 `tempDir`，因此請搭配 `fileUrl` 使用。`options.speaker` 可選擇已註冊的語音                                                                             |
| `emit(event, data)`                                                   | 進度事件。會送給 SSE 呼叫端；一般 HTTP 與 MCP 會忽略                                                                                                                                                                                                     |
| `signal`                                                              | `AbortSignal`，在逾時或呼叫端中斷連線時觸發。請將它傳給 `fetch`                                                                                                                                                                                             |
| `tempDir`                                                             | 每次呼叫專屬的暫存目錄，首次存取時建立                                                                                                                                                                                                                    |
| `fileUrl(path)`                                                       | 產生 `tempDir` 內某個檔案的下載 URL。若路徑不在本次呼叫的暫存目錄內會拋出錯誤                                                                                                                                                                                         |
| `log(…)`                                                              | 伺服器日誌，會加上函式名稱前綴                                                                                                                                                                                                                        |
| `fetch`、`env`、`config`、`dir`                                          | 主機的 `fetch`、`process.env`、`[functions.config]` 區段內容，以及函式目錄路徑                                                                                                                                                                           |
| `libs`                                                                | 內建輔助函式庫：`_` / `lodash`、`moment`、`math` / `mathjs`、`voca`、`chroma`、`json5`、`qs`、`bytes`、`ms`、`nanoid`、`md5`                                                                                                                             |

### 匯入

函式可以匯入 Node 內建模組、函式目錄內的同層檔案，以及兩個列入允許清單的套件：`sqlite3` 與 `sqlite-vec`。其他套件一律拒絕 — 內建的輔助函式庫請改用 `context.libs`。

```ts theme={null}
import fs from 'node:fs/promises'     // 內建模組 — 支援
import { helper } from './lib/util'   // 同層檔案 — 支援
import sqlite3 from 'sqlite3'         // 允許清單內的套件 — 支援
import axios from 'axios'             // 其他套件 — 拒絕
```

<Note>
  `sqlite-vec` 提供 macOS 與 Linux 的 x64、arm64 擴充功能，以及 Windows x64。它沒有 Windows arm64 版本，因此該平台的獨立版建置不提供這兩個 SQLite 匯入；本頁其餘功能在該平台仍可正常使用。
</Note>

### 檢索

只要 `tokenize`、`embedding` 與這兩個 SQLite 匯入，就能在單一函式內完成檢索：依 token 邊界切塊、為每個片段產生嵌入向量、把向量存進 `sqlite-vec` 虛擬資料表，再把最相近的結果交給 `completion`。

嵌入需要以嵌入模式載入的模型，chat 模型並不是。請為嵌入模型建立獨立的 `[[generators]]` 區塊，並讓 chat 模型維持在第一個 LLM：

```toml theme={null}
[[generators]]
type = "ggml-llm"

[generators.model]
repo_id = "nomic-ai/nomic-embed-text-v1.5-GGUF"
filename = "nomic-embed-text-v1.5.Q8_0.gguf"
embedding = true
pooling_type = "mean"
n_ctx = 2048
```

接著在每次 `embedding`、`tokenize`、`detokenize` 呼叫時把該 `repo_id` 當作 `model` 傳入，並在 `completion` 省略 `model`，讓答案由第一個 LLM 產生。完整的鍵列表請見[設定](/zh-Hant/buttress/configuration)。

索引本身就是一般的 SQLite：開啟 `sqlite3` 資料庫，用 `getLoadablePath()` 載入 `sqlite-vec` 擴充功能，再把每個向量以 float32 BLOB 寫入。`:memory:` 會建立拋棄式索引；改用檔案路徑（絕對路徑，或相對於函式目錄的路徑）則可在多次呼叫之間保留索引。

## 迭代開發

編輯會**在下一次呼叫時生效**。每次呼叫前，伺服器會檢查該函式模組圖中的每個檔案，只要有變動就重新載入，因此你不需要重新啟動伺服器。載入失敗的檔案會被記錄並略過，其他函式照常運作 — 如果某個函式突然不見了，請檢查伺服器日誌。

若想更快得到回饋，可設定 `hot_reload = true`。伺服器會額外監看目錄並在存檔時重新載入，因此檔案一存檔就會立刻記錄錯誤，函式數量也不必等到有人呼叫才更新。這純粹是加分項：每次呼叫前的檢查依然存在作為後盾；在不支援遞迴目錄監看的平台上，伺服器會記錄警告並退回原本的行為。

模組層級的狀態（處理常式外的 `let`）會在多次呼叫之間保留，並在你編輯檔案時重設。

## 呼叫函式

| 端點                                | 用途                                                                                  |
| --------------------------------- | ----------------------------------------------------------------------------------- |
| `GET /functions`                  | 列出可呼叫的函式及其 JSON Schema                                                              |
| `POST /functions/<name>`          | 執行單一函式。JSON body 即輸入物件，回應為 `{ "result": … }`                                        |
| `GET /functions/<name>?…`         | 以查詢字串作為輸入執行單一函式，不需要 body。參見[以 GET 呼叫](#以-get-呼叫)                                    |
| `POST /functions/<name>?stream=1` | 同上，但以 SSE 回傳：先送出 `context.emit` 的 `progress` 事件，再送出 `result` 或 `error`。`GET` 同樣支援串流 |
| `POST /functions/mcp`             | 透過 Streamable HTTP 提供 MCP（無狀態；`GET` 與 `DELETE` 回傳 405）                              |
| `GET /functions/files/<path>`     | 下載函式寫入暫存目錄的檔案                                                                       |
| `POST /functions/upload`          | 將輸入檔案暫存到伺服器，回傳 `{ "path", "url", "name", "size" }`                                  |

```bash theme={null}
curl -X POST http://localhost:2080/functions/host-info \
  -H 'Authorization: Bearer <workspace-access-token>' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

錯誤會以 `{ "error": { "code", "message" } }` 回傳：

| 代碼                        | 狀態  | 意義                               |
| ------------------------- | --- | -------------------------------- |
| `FUNCTION_NOT_FOUND`      | 404 | 找不到同名函式                          |
| `INVALID_INPUT`           | 400 | 無法將請求 body 或 `input` 查詢參數解析為輸入物件 |
| `FUNCTION_TIMEOUT`        | 504 | 呼叫超過期限                           |
| `FUNCTION_FAILED`         | 500 | 處理常式拋出錯誤                         |
| `FUNCTION_FILE_NOT_FOUND` | 404 | 下載目標不存在，或不在暫存根目錄內                |
| `UPLOAD_FAILED`           | 500 | 無法暫存上傳的檔案                        |

驗證遭拒時會回傳專屬代碼，參見[安全性](#安全性)。

### 以 GET 呼叫

`GET /functions/<name>` 執行同一個呼叫，而且完全不需要 body — 查詢字串就是輸入。瀏覽器網址列、webhook、`EventSource` 或單純的 `curl` 都能輕鬆產生這種請求：

```bash theme={null}
curl 'http://localhost:2080/functions/weather?city=Taipei&days=3&units=metric'
```

查詢字串的值一律是字串，因此函式宣告的 `meta.parameters` 同時也是型別轉換表。宣告為 `number` / `integer`、`boolean`、`array` 與 `object` 的屬性會被轉換，未宣告的則維持字串：

| 宣告型別                 | 查詢字串寫法                                                                 |
| -------------------- | ---------------------------------------------------------------------- |
| `number` / `integer` | `?n=3`                                                                 |
| `boolean`            | `?verbose=true`、`?verbose=1`，或只寫 `?verbose`；另一側則用 `false` 或 `0`        |
| `array`              | `?tag=a&tag=b`、`?tag=a,b` 或 `?tag=["a","b"]` — 元素依 schema 的 `items` 轉換 |
| `object`             | `?filter={"lang":"en"}`                                                |

轉換永遠不會拒絕請求。不符合宣告型別的值會原封不動傳入，而不會變成 `NaN`，因此處理常式看到的是呼叫端實際送出的內容，驗證的責任仍在處理常式。

若不論 schema 為何都想要精確的型別，請把整個物件以 JSON 放進 `input`。一般參數會疊加在它之上，行為與 multipart 呼叫完全相同：

```bash theme={null}
curl 'http://localhost:2080/functions/weather?input={"days":3}&city=Taipei'
```

`stream`、`token` 與 `access_token` 用於控制請求本身，永遠不會傳到處理常式 — 若函式真的需要名為 `token` 的輸入，請改用 `input` 傳入。

串流的用法相同：加上 `?stream=1`，或送出 `Accept: text/event-stream`（這是 `EventSource` 唯一能表達的方式）。由於 `EventSource` 同樣無法設定標頭，在已繫結的伺服器上請以 `?token=…` 傳遞 token。

<Note>
  每個函式都支援 GET，且回應皆帶有 `no-store`。HTTP 預期 GET 可以安全地重複執行，而只有你知道自己的函式是否如此 — 請依函式實際的行為選擇方法。
</Note>

### 傳入與傳出檔案

二進位結果以 URL 傳遞，而不是直接回傳內容。請寫入 `context.tempDir`、回傳 `context.fileUrl(path)`，呼叫端就能用手上既有的 `Authorization` 標頭抓取 `<base><url>`：

```ts theme={null}
const { path } = await context.buttress.synthesize({ text: 'Hello from Buttress' })
return { audio: context.fileUrl(path) }
```

下載與呼叫共用同一道驗證關卡，且只會解析到函式暫存根目錄之內 — 路徑穿越、目錄與無法解碼的路徑，都與檔案不存在無法區分 — 並會保留到約 24 小時後的暫存清理為止。

二進位輸入則走相反方向，而且只需一次請求。以 `multipart/form-data` 呼叫函式，每個檔案欄位都會暫存到該次呼叫的暫存目錄，並將伺服器本機路徑以欄位名稱注入輸入中：

```bash theme={null}
curl -X POST http://localhost:2080/functions/transcribe-media -F file=@interview.mp4
```

函式會收到 `input.file` 作為路徑，可直接交給 `ffmpeg`，因此整段流程都能遠端完成，不需要存取檔案系統。若要傳入具型別的值，加上 `-F input='{"model":"…"}'`；其他一般欄位會以字串傳入，檔案路徑在名稱衝突時優先，重複的檔案欄位會變成路徑陣列。`?stream=1` 也可以搭配 multipart 使用。

若想暫存一次檔案並在多次呼叫中重複使用，請單獨上傳，再把回傳的 `path` 放進 JSON 呼叫：

```bash theme={null}
curl -F file=@interview.mp4 http://localhost:2080/functions/upload
# → { "path": "…", "url": "…", "name": "interview.mp4", "size": 20972112 }
```

兩種路徑都會將用戶端檔名淨化為純檔名，暫存檔案與函式輸出共用同一道驗證關卡與 24 小時清理機制，而請求大小則受 `[server] max_body_size`（預設 50 MB）限制 — 處理大型媒體時請調高。

## 連線 agent

[BRICKS CLI](/zh-Hant/cli) 的 `bricks buttress mcp-config` 會產生伺服器 MCP 端點的 `.mcp.json` 項目。它會在區域網路上探索伺服器、簽發 workspace access token，並在不影響你其他 MCP 伺服器的前提下合併該項目：

```bash theme={null}
bricks buttress mcp-config --write
```

```json theme={null}
{
  "mcpServers": {
    "buttress-functions": {
      "url": "http://192.168.1.24:2080/functions/mcp",
      "headers": { "Authorization": "Bearer <workspace-access-token>" }
    }
  }
}
```

用 `--url` 指定特定主機，未啟用驗證的伺服器則可省略 token：

```bash theme={null}
bricks buttress mcp-config --url http://buttress.local:2080 --no-token
```

<Warning>
  寫入的項目內嵌了長效的 workspace access token。請將 `.mcp.json` 視為機密，不要納入版本控制。
</Warning>

探索流程不會為未回報你的 workspace 的主機簽發 token：未繫結或屬於其他 workspace 的伺服器都算不符，此時指令會直接拒絕，而不是把憑證交給任何回應探測的機器。若你確實要指定該伺服器，請改用 `--url`。完整選項參見 [`bricks buttress mcp-config`](/zh-Hant/cli/commands#bricks-buttress-mcp-config)。

## 安全性

函式會在主機上執行程式碼並產生子處理程序，因此這個介面採**預設拒絕**，不像推論端點那樣沿用「未繫結即開放」的原則。

* **未繫結的伺服器** — 每次呼叫都會以 `403 FUNCTIONS_UNAUTHENTICATED_DISABLED` 拒絕。
* **已繫結的伺服器** — 與其他資料路徑一樣需要 workspace access token，參見 [Workspace 繫結](/zh-Hant/buttress/workspace-binding)。
* **瀏覽器請求** — 只要被瀏覽器標記為跨站，就會以 `403 FUNCTIONS_ORIGIN_BLOCKED` 拒絕，除非其來源已列在 `[functions] cors_allowed_origins` 中。agent、MCP 用戶端與 `curl` 不受影響。

下載與上傳和呼叫共用同一道關卡。

瀏覽器檢查會讀取兩個標頭，因為單靠 `Origin` 無法涵蓋 GET 呼叫。CORS 請求會帶有 `Origin`，並比對允許清單。而 no-CORS 的子資源載入（`<img src>`、`<script src>`、預先擷取）完全不會送出 `Origin`，否則就會被當成非瀏覽器的呼叫端；瀏覽器會為這類請求標記 `Sec-Fetch-Site`，這是指令碼無法設定的禁止標頭，其中 `cross-site` 與 `same-site` 正好涵蓋 `Origin` 遺漏的部分。沒有這個標頭仍然代表「不是瀏覽器」。

<Warning>
  列出來源只對本身帶有來源的請求有用。no-CORS 載入沒有來源可比對，因此只有 `cors_allowed_origins = "*"` 能放行這類請求。
</Warning>

若要在未繫結 workspace 的伺服器上執行函式，必須明確選擇加入：

```toml theme={null}
[functions]
allow_unauthenticated = true
```

<Warning>
  `allow_unauthenticated = true` 會讓任何能連到該連接埠的人執行所有函式。請只在受信任的網路中使用。
</Warning>

函式檔案是**受信任的輸入**，就跟設定檔本身一樣。它們在乾淨的全域環境中執行（沒有現成的 `process` 或 `require`），但這是為了清楚，而非隔離：拿到 `spawn` 的函式能做的事，與伺服器處理程序完全相同。切勿將你未撰寫或未審閱的程式碼放進函式目錄。

每次呼叫都帶有期限，來自 `meta.timeout` 或 `[functions] default_timeout`。期限到達時（或呼叫端中斷連線時），`context.signal` 會觸發，該次呼叫產生的所有處理程序都會被終止。同步阻塞事件迴圈的程式碼無法被中斷，因此請讓處理常式維持非同步。

### 自訂驗證

在函式目錄放入 `_auth.ts`（或 `.js`），即可在每個 `/functions` 端點前加上你自己的邏輯。它的形式與函式檔案相同，而 `meta.mode` 決定它與上述 workspace 關卡的組合方式：

* **`both`**（預設）— workspace 驗證先執行且維持原樣，你的處理常式接在後面作為額外關卡。它只能收緊存取：逐函式允許清單、主體檢查、稽核記錄。
* **`override`** — 由你的處理常式獨自決定，`allow_unauthenticated` 不再有作用。呼叫端提供的 workspace token 仍會被驗證並放入 `request.workspaceAuth`，因此你可以同時支援 workspace token 與其他憑證。

```ts theme={null}
export const meta: ButtressAuthMeta = { mode: 'override' }

export default async function (request: ButtressAuthRequest, context: ButtressAuthContext) {
  if (request.workspaceAuth.authenticated) return true    // workspace token 照常運作
  const keys = context.config.api_keys                    // 來自 [functions.config]
  if (Array.isArray(keys) && keys.includes(request.headers['x-api-key'])) return true
  return { ok: false, status: 401, error: 'Invalid or missing API key' }
}
```

處理常式會收到 `{ method, path, name?, headers, query, token, workspaceAuth }` — 只有 `GET` / `POST /functions/<name>` 才會帶 `name`，因為 MCP 的工具名稱位於關卡不會解析的 JSON-RPC body 中 — 以及精簡的 context：`log`、`fetch`、`env`、`config`、`dir` 與 `libs`。沒有 `spawn`，也沒有 `buttress`。

只有回傳 `true` 或 `{ ok: true }` 才會放行。其他任何回傳值都會以 `403 FUNCTIONS_AUTH_REJECTED` 拒絕，或使用你回傳的 `status` 與 `error`。驗證永遠不會在失敗時放行：只要 `_auth` 檔案存在卻無法載入，每次呼叫都會以 `500 FUNCTIONS_AUTH_UNAVAILABLE` 拒絕；處理常式拋出錯誤則以 `500 FUNCTIONS_AUTH_ERROR` 拒絕。瀏覽器跨站阻擋一律先執行，因此寬鬆的 `_auth` 也無法重新開啟它。

`_auth.ts` 與函式檔案一樣採延遲重新載入，刪除它就會回到單純的 workspace 驗證。

<Note>
  `bricks buttress mcp-config` 簽發的是 workspace token。若你的 `override` 模式 `_auth` 忽略 `workspaceAuth`，這些 token 就會失效 — 如果 agent 仍以此方式連線，請保留上面範例中處理 workspace token 的分支。
</Note>

## 檢視執行狀況

你不需要伺服器日誌的存取權也能了解函式的行為。`GET /buttress/status` 會回傳 `functions` 區段，內含啟動以來的計數，以及呼叫（依 HTTP、SSE 或 MCP 分類，含耗時與失敗原因）、上傳、下載與驗證決策的近期記錄。同一份資料也會在伺服器的 [`/status` 儀表板](/zh-Hant/buttress/installation#確認運作)上呈現為 **Local Functions** 卡片。

記錄的只有中繼資料 — 名稱、路徑、大小、結果代碼、主體 id，絕不包含 token 或金鑰。

探索只公告存在與否，不公告內容：`serverInfo.functions` 為 `{ enabled, count }`。工具清單本身不會被公告，因為整份 `serverInfo` 必須放得進一個 UDP 封包。參見[區域網路自動探索](/zh-Hant/buttress/autodiscovery)。

## 範例

伺服器套件在 `config/function-samples/` 中提供可直接複製的範例。把其中一個複製到你的函式目錄再呼叫即可，不需要重新啟動。

| 範例                    | 示範內容                                                        | 前置需求                                            |
| --------------------- | ----------------------------------------------------------- | ----------------------------------------------- |
| `host-info.ts`        | 最小可用的函式：一個 Node 內建模組加上 `context.libs`                       | 無                                               |
| `summarize-text.ts`   | 以 `context.buttress.completion` 呼叫本伺服器的 LLM                 | 一個 LLM `[[generators]]` 項目                      |
| `simple-rag.ts`       | 以 token 切塊、產生嵌入向量，並用 `sqlite3` + `sqlite-vec` 檢索後再作答        | 一個 chat LLM 與一個 GGML 嵌入 `[[generators]]` 項目     |
| `transcribe-media.ts` | `context.spawn`（ffmpeg）、暫存目錄、SSE 進度與 STT                    | `PATH` 上有 `ffmpeg`，加上一個 STT `[[generators]]` 項目 |
| `text-to-speech.ts`   | `context.buttress.synthesize` 搭配 `context.fileUrl` 產生可下載的結果 | 一個 `onnx-tts` 或 `ggml-tts` `[[generators]]` 項目  |
| `_auth.ts`            | 自訂驗證：保留 workspace token 的同時加上靜態 API key                     | 請先閱讀檔案開頭的說明                                     |

`_auth.ts` 不是函式 — 複製它會改變每個 `/functions` 端點驗證呼叫端的方式。

## 後續步驟

<CardGroup cols={2}>
  <Card title="設定" icon="gear" href="/zh-Hant/buttress/configuration">
    完整 TOML 參考，包含函式會呼叫的 generator。
  </Card>

  <Card title="Workspace 繫結" icon="key" href="/zh-Hant/buttress/workspace-binding">
    繫結伺服器，讓函式呼叫需要 workspace token。
  </Card>
</CardGroup>
