> ## 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](/ja/buttress) サーバーの指定ディレクトリに置く `.ts` / `.js` ファイルです。1 つのファイルが MCP ツール**であり**、同時に HTTP エンドポイントにもなります。これにより、エージェント（あるいは任意の HTTP クライアント）がサーバー上で処理を実行できます — `ffmpeg` の呼び出し、そのサーバー自身の LLM / 音声認識 / 音声合成 generator の利用、社内サービスへの接続など。書くのはファイル 1 つだけで、別途サービスを立てる必要はありません。

<Warning>
  ローカル関数は実験的機能です。エンドポイント、関数ファイルの契約、`context` API、カスタム `_auth`、`[functions]` 設定キーは、非推奨期間を挟まずにリリース間で変更される可能性があります。サーバーは起動時にこの注意を表示します。これを前提に開発する場合は `bricks-buttress` のバージョンを固定し、アップグレード後は本ページを読み直してください。
</Warning>

ローカル関数は Buttress の他の機能とは性質が異なります。他の部分では、[BRICKS Foundation](/ja/foundation) デバイスが本来自分で実行する generator を Buttress がオフロードします。ローカル関数は**サーバー上**で動き、サーバーの運用者が書き、アプリのランタイムではなくエージェントや 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` として渡される自由形式の値                               |

最初の 3 つのキーには環境変数の同等物があります: `ENABLE_FUNCTIONS_ENDPOINT=1`、`BUTTRESS_FUNCTIONS_DIR=<dir>`、`BUTTRESS_FUNCTIONS_HOT_RELOAD=1`。`allow_unauthenticated` に対応するのは `BUTTRESS_FUNCTIONS_ALLOW_UNAUTHENTICATED=1` です。

<Note>
  `enabled = true` でもディレクトリが未設定の場合、サーバーはパスを推測せず、警告をログに出してローカル関数を無効のままにします。
</Note>

### サーバーが生成するファイル

サーバーは起動のたびに、ディレクトリがなければ作成し、`buttress-functions.d.ts` を書き出します。ここには `ButtressFunctionMeta`、`ButtressFunctionContext`、`ButtressAuth*` などのアンビエント型が入っています。このファイルは毎回再生成されるため、常に動作中のサーバーと一致します。本ページと手元のサーバーが食い違う場合は、このファイルを正としてください。

ディレクトリにまだ関数が 1 つもない場合は、`tsconfig.json` とコメント付きの `_example.ts` も生成されます。どちらも一度編集すれば上書きされることはありません。

## 関数を書く

1 ファイルが 1 関数で、**ファイル名がツール名**になります — `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 クライアントにそのまま渡されます。入力の説明を 1 度書けば、すべての面で同じ内容になります。省略した場合、ツールは空の object スキーマを公開します。
* JSON にシリアライズできる値を返してください。ファイルを返す場合は `context.tempDir` に書き出し、`context.fileUrl(path)` を返します — [ファイルの入出力](#ファイルの入出力)を参照。
* 関数名は英数字で始まり、以降は英数字・`-`・`_` を使えます。

検出時には、`_` で始まるファイル、ドットファイル、`*.d.ts`、`*.test.*`、`*.spec.*`、およびサブディレクトリがスキップされます — サブディレクトリは import するヘルパーモジュール用です。`_auth.ts` はアンダースコア規則の唯一の例外で、有効なファイルとして扱われ、すべてのエンドポイントの認証方法を変更します。[カスタム認証](#カスタム認証)を参照してください。同名の `.ts` と `.js` がある場合は `.ts` が優先されます。

`mcp`、`files`、`upload` は予約名です。`/functions/mcp`、`/functions/files/*`、`/functions/upload` がルートだからで、この名前のファイルはスキップされます。

### Context

第 2 引数には、関数から使えるものがすべて入っています。

| メンバー                                                                  | 内容                                                                                                                                                                                                                                                                           |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `spawn(cmd, args?, opts?)`                                            | 子プロセスを実行し、`{ code, signal, stdout, stderr, truncated }` を返します。**終了コードが 0 以外でも resolve します** — `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` を渡さない限り思考は出力されません。その他のキーは backend にそのまま渡ります |
| `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`。タイムアウト時、または呼び出し元の切断時に abort されます。`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`                                                                                                                                                                   |

### import

関数から import できるのは、Node の組み込みモジュール、関数ディレクトリ内の同階層ファイル、そして許可リストに載っている 2 つのパッケージ `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 import も利用できません。それ以外の機能はこのページのとおり動作します。
</Note>

### 検索

`tokenize`、`embedding`、そして 2 つの SQLite import があれば、検索を 1 つの関数の中で完結できます。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 に回答させます。キーの一覧は[設定](/ja/buttress/configuration)を参照してください。

インデックス自体は通常の SQLite です。`sqlite3` のデータベースを開き、`getLoadablePath()` で `sqlite-vec` 拡張を読み込み、各ベクトルを float32 の BLOB として書き込みます。`:memory:` なら使い捨てのインデックスになり、ファイルパス（絶対パス、または関数ディレクトリからの相対パス）を指定すれば呼び出しをまたいでディスク上に保持されます。

## 反復して開発する

編集は**次の呼び出しで反映**されます。サーバーは呼び出しのたびに、その関数のモジュールグラフに含まれるファイルを確認し、変更があればリロードします。サーバーの再起動は不要です。読み込みに失敗したファイルはログに記録されてスキップされ、他の関数はそのまま動き続けます — 関数が一覧から消えたらサーバーログを確認してください。

より速いフィードバックが欲しい場合は `hot_reload = true` を設定します。サーバーはディレクトリも監視して保存時にリロードするため、壊れたファイルは保存した瞬間にログに出て、関数の数も呼び出しを待たずに更新されます。これは純粋な追加機能です。呼び出しごとのチェックは引き続き働き、再帰的なディレクトリ監視に対応しないプラットフォームでは警告をログに出して従来どおりの動作に戻ります。

モジュールレベルの状態（ハンドラー外の `let`）は呼び出しをまたいで保持され、ファイルを編集するとリセットされます。

## 関数を呼び出す

| エンドポイント                           | 用途                                                                                         |
| --------------------------------- | ------------------------------------------------------------------------------------------ |
| `GET /functions`                  | 呼び出せる関数を JSON Schema 付きで一覧表示                                                               |
| `POST /functions/<name>`          | 関数を 1 つ実行。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"]` — 要素はスキーマの `items` に従って変換されます |
| `object`             | `?filter={"lang":"en"}`                                                  |

変換が呼び出しを拒否することはありません。宣言された型に合わない値は `NaN` にされるのではなくそのまま渡されるので、ハンドラーは呼び出し元が実際に送った内容を受け取り、検証の責任は引き続きハンドラーにあります。

スキーマに関係なく正確な型を渡したいときは、オブジェクト全体を 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 時間後の掃除まで残ります。

バイナリの入力は逆方向で、しかも 1 リクエストで済みます。関数に `multipart/form-data` を POST すると、各ファイルフィールドがその呼び出しの作業ディレクトリに退避され、サーバー上のパスがフィールド名で入力に注入されます。

```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）で制限されるため、大きなメディアを扱うときは引き上げてください。

## エージェントを接続する

[BRICKS CLI](/ja/cli) の `bricks buttress mcp-config` は、サーバーの MCP エンドポイント用の `.mcp.json` エントリを書き出します。LAN 上のサーバーを検出し、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`](/ja/cli/commands#bricks-buttress-mcp-config) を参照してください。

## セキュリティ

関数はホスト上でコードを実行しプロセスを起動するため、この面は**デフォルト拒否**です。推論エンドポイントのような「未バインド＝オープン」は引き継ぎません。

* **未バインドのサーバー** — すべての呼び出しが `403 FUNCTIONS_UNAUTHENTICATED_DISABLED` で拒否されます。
* **バインド済みのサーバー** — 他のデータ経路とまったく同じく workspace access token が必要です。[Workspace バインド](/ja/buttress/workspace-binding)を参照してください。
* **ブラウザーからのリクエスト** — ブラウザーがクロスサイトと印を付けたリクエストは、そのオリジンが `[functions] cors_allowed_origins` に列挙されていない限り `403 FUNCTIONS_ORIGIN_BLOCKED` で拒否されます。エージェント、MCP クライアント、`curl` は影響を受けません。

ダウンロードとアップロードも呼び出しと同じガードの後ろにあります。

ブラウザー判定は 2 つのヘッダーを見ます。`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` エンドポイントの前に独自ロジックを挟めます。形は関数ファイルと同じで、上記の workspace ゲートとの組み合わせ方は `meta.mode` で決まります。

* **`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 }` を受け取ります（`name` が入るのは `GET` / `POST /functions/<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 です。`workspaceAuth` を無視する `override` モードの `_auth` を置くと、それらの token は使えなくなります — エージェントが引き続きその方法で接続するなら、上のサンプルにある workspace token の分岐を残してください。
</Note>

## 実行状況を確認する

関数の挙動を知るのにサーバーログへのアクセスは必要ありません。`GET /buttress/status` には `functions` セクションがあり、起動以降のカウンターと、呼び出し（HTTP / SSE / MCP のどれか、所要時間と失敗理由付き）、アップロード、ダウンロード、認証判定の直近の履歴が含まれます。同じデータはサーバーの [`/status` ダッシュボード](/ja/buttress/installation#動作確認)にも **Local Functions** カードとして表示されます。

記録されるのはメタデータだけです — 名前、パス、サイズ、結果コード、サブジェクト ID。token や鍵が記録されることはありません。

検出が広告するのは存在の有無だけで、中身ではありません。`serverInfo.functions` は `{ enabled, count }` です。`serverInfo` 全体が UDP データグラムに収まる必要があるため、ツール一覧そのものが広告されることはありません。[LAN 自動検出](/ja/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 キーを追加するカスタム認証                           | まずファイル冒頭の説明を読むこと                                  |

`_auth.ts` は関数ではありません — コピーすると、すべての `/functions` エンドポイントの認証方法が変わります。

## 次のステップ

<CardGroup cols={2}>
  <Card title="設定" icon="gear" href="/ja/buttress/configuration">
    関数が呼び出す generator を含む TOML の完全リファレンス。
  </Card>

  <Card title="Workspace バインド" icon="key" href="/ja/buttress/workspace-binding">
    サーバーをバインドして、関数呼び出しに workspace token を必須にする。
  </Card>
</CardGroup>
