Руководство по самостоятельному размещению OOMOL OpenConnector
OOMOL OpenConnector — это open-source connector service, который можно self-host. Используйте его, когда агентам или внутренним инструментам нужно вызывать реальные внешние сервисы, а provider credentials, permissions и execution history должны оставаться в вашей среде. Он предоставляет типизированные actions для сервисов GitHub, Gmail, Notion, Hacker News, Ably, Abstract, A-Leads и других через MCP или HTTP.
Агенты видят schemas, scopes, execution status и безопасные account labels; raw provider tokens, action permissions и execution history остаются внутри вашей deployment boundary.
Что это даёт
- Runtime, который предоставляет provider actions через MCP, HTTP, OpenAPI и web console.
- Credential storage для API keys, custom credentials, OAuth2 connections и no-auth providers.
- Typed action schemas, чтобы агенты могли понять, что они могут вызвать, до вызова.
- Connection identity и scopes, чтобы users и agents видели, от имени какого account будет выполняться action.
- Temporary file transit для actions, которым нужны file URLs.
- Recent run logs с редактированными summaries входа и provider errors.
- Provider catalog с local executors, которые загружаются только при использовании action.
Начните с выбора места запуска runtime, затем настройте storage, access control, provider connections и agent-facing MCP или HTTP entry point.
Выберите deployment target
| Target | Когда использовать | Storage |
|---|---|---|
| Docker Compose | Нужен самый быстрый local или single-server deployment. | Docker volume, смонтированный в /app/data, с SQLite в /app/data/connect.sqlite. |
| Source runtime | Вы разрабатываете OOMOL OpenConnector или provider executors. | Local ./data/connect.sqlite, если не задан OOMOL_CONNECT_DATA_DIR. |
| Cloudflare Workers | Нужен Cloudflare-hosted deployment runtime state и metadata. | D1 для runtime records и R2 для temporary transit files. |
Подготовьте runtime
Перед deployment определите эти значения:
| Значение | Почему это важно |
|---|---|
OOMOL_CONNECT_ADMIN_TOKEN | Защищает web console, /api и local API reference, когда они доступны за пределами вашего shell. |
OOMOL_CONNECT_ENCRYPTION_KEY | Шифрует сохранённые provider credentials и OAuth client secrets. Храните его в secret manager. |
OOMOL_CONNECT_ORIGIN | Задаёт public origin для OAuth callback URLs. Требуется, когда browser достигает runtime через tunnel, domain или Worker URL. |
| Runtime tokens | Аутентифицируют вызовы agents и clients к /v1 и /mcp. Создавайте их на вкладке Access web console или через admin API. |
| Action policy | Ограничивает actions, которые можно выполнять через /v1 и MCP. Используйте OOMOL_CONNECT_ALLOWED_ACTIONS и OOMOL_CONNECT_BLOCKED_ACTIONS. |
Считайте runtime database чувствительной. Без OOMOL_CONNECT_ENCRYPTION_KEY OOMOL OpenConnector всё равно работает, но provider secrets хранятся в чувствительном local SQLite file.
Запуск с Docker Compose
Клонируйте репозиторий OOMOL OpenConnector, перейдите в каталог проекта и запустите runtime:
git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
docker compose up --build
Откройте web console:
http://localhost:3000
Откройте сгенерированную API reference:
http://localhost:3000/docs
Проверьте runtime no-auth action:
curl -s -X POST http://localhost:3000/v1/actions/hackernews.get_top_stories \
-H 'content-type: application/json' \
-d '{"input":{}}'
Docker Compose хранит runtime state в volume connector-data. Container хранит SQLite здесь:
/app/data/connect.sqlite
Для private server deployment задайте как минимум admin token и encryption key перед запуском:
Выполняйте эти примеры в Bash. Вводите реальные учётные данные только в скрытых приглашениях; не вставляйте их в команды оболочки или журналы команд.
(
set -eu
read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
echo
test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
export OOMOL_CONNECT_ADMIN_TOKEN
read -r -s -p "OOMOL_CONNECT_ENCRYPTION_KEY: " OOMOL_CONNECT_ENCRYPTION_KEY
echo
test -n "$OOMOL_CONNECT_ENCRYPTION_KEY"
export OOMOL_CONNECT_ENCRYPTION_KEY
docker compose up --build
)
Когда runtime доступен через public domain или tunnel, задайте также public origin:
(
set -eu
export OOMOL_CONNECT_ORIGIN="https://connect.example.com"
read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
echo
test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
export OOMOL_CONNECT_ADMIN_TOKEN
read -r -s -p "OOMOL_CONNECT_ENCRYPTION_KEY: " OOMOL_CONNECT_ENCRYPTION_KEY
echo
test -n "$OOMOL_CONNECT_ENCRYPTION_KEY"
export OOMOL_CONNECT_ENCRYPTION_KEY
read -r -s -p "OOMOL_CONNECT_RUNTIME_TOKEN: " OOMOL_CONNECT_RUNTIME_TOKEN
echo
test -n "$OOMOL_CONNECT_RUNTIME_TOKEN"
export OOMOL_CONNECT_RUNTIME_TOKEN
docker compose up --build
)
Docker image внутри container привязывается к 0.0.0.0. Контролируйте внешний доступ через host firewall, reverse proxy или container platform.
Защитите admin и runtime access
Admin HTTP clients вызывают /api, /docs или web console с:
Authorization: Bearer replace-with-an-admin-token
Создавайте runtime tokens для agents и SDK-style clients на вкладке Access web console. Token показывается один раз; хранится только hash.
Его также можно создать через admin API:
Для примеров запросов с аутентификацией нужны Python 3 и curl версии 7.76.0 или новее, поддерживающий параметр —fail-with-body, используемый во всех этих примерах. Секреты вводятся скрыто и передаются curl через стандартный ввод, не попадая в аргументы командной строки или историю оболочки.
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
payload = {'name': 'Claude Desktop'}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/api/runtime-tokens', '--header', 'content-type: application/json'],
input=config, text=True, check=True,
)
PY
Runtime clients затем вызывают /v1 или /mcp с:
Authorization: Bearer oct_...
Для bootstrap scripts и backward compatibility OOMOL_CONNECT_RUNTIME_TOKEN всё ещё принимается:
(
set -eu
read -r -s -p "OOMOL_CONNECT_ADMIN_TOKEN: " OOMOL_CONNECT_ADMIN_TOKEN
echo
test -n "$OOMOL_CONNECT_ADMIN_TOKEN"
export OOMOL_CONNECT_ADMIN_TOKEN
read -r -s -p "OOMOL_CONNECT_RUNTIME_TOKEN: " OOMOL_CONNECT_RUNTIME_TOKEN
echo
test -n "$OOMOL_CONNECT_RUNTIME_TOKEN"
export OOMOL_CONNECT_RUNTIME_TOKEN
docker compose up --build
)
Ограничьте actions, которые агенты могут выполнять:
OOMOL_CONNECT_ALLOWED_ACTIONS="hackernews.*,github.get_current_user" docker compose up --build
Блокируйте конкретные actions, даже если более широкая allowlist включает их:
OOMOL_CONNECT_ALLOWED_ACTIONS="github.*" \
OOMOL_CONNECT_BLOCKED_ACTIONS="github.delete_repository" \
docker compose up --build
Подключите API-key provider
GitHub — компактный пример API key, потому что он может использовать personal access token.
Проверьте provider contract:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/providers/github'],
input=config, text=True, check=True,
)
PY
Сохраните default GitHub connection:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
payload = {'authType': 'api_key', 'values': {'apiKey': None}}
payload["values"]["apiKey"] = getpass.getpass("GitHub API key: ")
if not payload["values"]["apiKey"]:
raise SystemExit("An API key is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'PUT', 'http://localhost:3000/api/connections/github', '--header', 'content-type: application/json'],
input=config, text=True, check=True,
)
PY
Вызовите GitHub через runtime:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
raise SystemExit("A token is required")
payload = {'input': {}}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/v1/actions/github.get_current_user', '--header', 'content-type: application/json'],
input=config, text=True, check=True,
)
PY
Проверьте настроенные connections и safe account identity, видимую агентам:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/connections'],
input=config, text=True, check=True,
)
PY
Именованные подключения
Добавьте connectionName, когда одному provider нужно несколько accounts:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
payload = {'authType': 'api_key', 'connectionName': 'work', 'values': {'apiKey': None}}
payload["values"]["apiKey"] = getpass.getpass("GitHub API key: ")
if not payload["values"]["apiKey"]:
raise SystemExit("An API key is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'PUT', 'http://localhost:3000/api/connections/github', '--header', 'content-type: application/json'],
input=config, text=True, check=True,
)
PY
Выберите этот account при execution:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
raise SystemExit("A token is required")
payload = {'input': {}}
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
config += "data = " + json.dumps(json.dumps(payload)) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'POST', 'http://localhost:3000/v1/actions/github.get_current_user', '--header', 'x-oo-connector-alias: work', '--header', 'content-type: application/json'],
input=config, text=True, check=True,
)
PY
Query parameter alias также принимается.
Подключите OAuth provider
OAuth providers используют ваше собственное provider OAuth app. Сначала перечислите providers с поддержкой OAuth и скопируйте expectedRedirectUri для service:
curl -s http://localhost:3000/api/oauth/configs \
-H 'authorization: Bearer replace-with-an-admin-token'
С портом по умолчанию GitHub использует этот callback URL:
http://localhost:3000/oauth/callback
Вставьте точный callback URL, возвращённый /api/oauth/configs, в provider OAuth app. Если вы меняете PORT, HOST или открываете runtime через другой origin, задайте OOMOL_CONNECT_ORIGIN перед запуском runtime и снова прочитайте callback URL.
Сохраните OAuth client локально:
curl -s -X PUT http://localhost:3000/api/oauth/configs/github \
-H 'authorization: Bearer replace-with-an-admin-token' \
-H 'content-type: application/json' \
-d '{"clientId":"...","clientSecret":"..."}'
Запустите authorization:
curl -s -X POST http://localhost:3000/api/oauth/authorizations \
-H 'authorization: Bearer replace-with-an-admin-token' \
-H 'content-type: application/json' \
-d '{"service":"github"}'
Откройте возвращённый authorizationUrl в браузере. После redirect provider на callback URL OOMOL OpenConnector сохранит OAuth credential как default connection.
Чтобы сохранить OAuth result как named connection, включите connectionName:
curl -s -X POST http://localhost:3000/api/oauth/authorizations \
-H 'authorization: Bearer replace-with-an-admin-token' \
-H 'content-type: application/json' \
-d '{"service":"github","connectionName":"work"}'
Передайте tools агенту
Для MCP-capable clients укажите client:
http://localhost:3000/mcp
MCP server предоставляет discovery-oriented tools:
list_appssearch_actionsget_action_guideexecute_action
Просмотрите MCP tool metadata:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/mcp/tools'],
input=config, text=True, check=True,
)
PY
Для HTTP clients используйте /v1 runtime API:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("Runtime token: ")
if not token:
raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/v1/actions'],
input=config, text=True, check=True,
)
PY
У каждого action есть локальное Markdown-руководство с input schema, scopes, provider permissions, current connection identity и request examples:
python3 - <<'PY'
import getpass
import json
import subprocess
import warnings
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("OOMOL_CONNECT_ADMIN_TOKEN: ")
if not token:
raise SystemExit("A token is required")
config = "header = " + json.dumps("authorization: Bearer " + token) + "\n"
subprocess.run(
['curl', '--disable', '--silent', '--show-error', '--fail-with-body', '--config', '-', '--request', 'GET', 'http://localhost:3000/api/actions/github.get_current_user/agent.md'],
input=config, text=True, check=True,
)
PY
Web console также может копировать примеры cURL, TypeScript и agent prompt для каждого action.
Запуск из source
Используйте source workflow, когда разрабатываете OOMOL OpenConnector или provider executors. Используйте Node.js 22 или новее.
git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
npm install
npm run build:web
npm run dev
npm install и npm run dev создают локальные generated files, когда они отсутствуют или устарели.
При запуске из source runtime state хранится в:
./data/connect.sqlite
Используйте другой data directory с:
OOMOL_CONNECT_DATA_DIR=/path/to/data npm run dev
Задайте те же environment variables admin, encryption, origin, runtime token и action policy, которые описаны выше.
Deploy в Cloudflare Workers
Cloudflare Workers поддерживается как deployment target для metadata и runtime-state.
Клонируйте repository, создайте ресурсы Cloudflare и deploy:
git clone https://github.com/oomol-lab/open-connector.git
cd open-connector
cp wrangler.example.jsonc wrangler.local.jsonc
npm install
npm run generate:catalog
npm run build:web
npx wrangler d1 create oomol-connect
npx wrangler r2 bucket create oomol-connect-transit-files
Перед deploy обновите игнорируемый файл wrangler.local.jsonc, указав D1 database_id, возвращённый Cloudflare.
Во время первоначальной настройки Worker должен оставаться закрытым: в локальной конфигурации Wrangler задайте workers_dev и preview_urls как false и не настраивайте публичные маршруты или собственные домены. Не открывайте публичный доступ, пока не заданы все три секрета ниже.
npx wrangler d1 migrations apply oomol-connect --remote --config wrangler.local.jsonc
npm run deploy:cloudflare
Задайте secrets через Wrangler:
npx wrangler secret put OOMOL_CONNECT_ADMIN_TOKEN --config wrangler.local.jsonc
npx wrangler secret put OOMOL_CONNECT_RUNTIME_TOKEN --config wrangler.local.jsonc
npx wrangler secret put OOMOL_CONNECT_ENCRYPTION_KEY --config wrangler.local.jsonc
После настройки всех трёх секретов включите нужный публичный маршрут в локальной конфигурации и выполните повторное развёртывание.
Cloudflare использует те же имена environment variables для origin, auth tokens, action policy, transit file limits и credential encryption. PORT, HOST и OOMOL_CONNECT_DATA_DIR применяются только к local Node.
Worker runtime обслуживает catalog metadata, metadata endpoints /api и /v1, connections, runtime tokens, OAuth config и state, transit files на R2 и generated provider action executor registry. Настройте R2 lifecycle rule для transit bucket, если хотите автоматически очищать непрочитанные expired transit files.
Эксплуатация deployment
Держите эти records доступными для support и recovery:
| Record | Где найти |
|---|---|
| Runtime database | Docker volume, local OOMOL_CONNECT_DATA_DIR или Cloudflare D1. |
| Temporary transit files | OOMOL_CONNECT_DATA_DIR/files для local runtime или Cloudflare R2. |
| Admin token and encryption key | Ваш secret manager. OOMOL OpenConnector не хранит encryption key за вас. |
| Runtime token prefix | Вкладка Access web console или /api/runtime-tokens. Полные runtime tokens показываются только один раз. |
| Execution history | Recent runs в web console или GET /api/runs. |
Для file-upload actions local transit files хранятся в OOMOL_CONNECT_DATA_DIR/files и очищаются по age. Настройте lifetime и upload size через OOMOL_CONNECT_TRANSIT_FILE_TTL_SECONDS и OOMOL_CONNECT_TRANSIT_FILE_MAX_BYTES.
Устранение неполадок
| Симптом | Что проверить |
|---|---|
Web console или /api возвращает unauthorized. | Отправьте Authorization: Bearer <admin-token> и подтвердите, что OOMOL_CONNECT_ADMIN_TOKEN соответствует running environment. |
/v1 или /mcp возвращает unauthorized. | Используйте runtime token, созданный на вкладке Access или через POST /api/runtime-tokens. Admin tokens предназначены для admin surfaces, а не runtime clients. |
| OAuth redirect уходит на неправильный host. | Задайте OOMOL_CONNECT_ORIGIN как origin, который users открывают в browser, перезапустите runtime, затем скопируйте свежий expectedRedirectUri из /api/oauth/configs. |
| Action не находит credentials. | Проверьте /api/connections, выбранный x-oo-connector-alias и доступность connection. |
| Action заблокирован. | Проверьте OOMOL_CONNECT_ALLOWED_ACTIONS и OOMOL_CONNECT_BLOCKED_ACTIONS. Blocked actions имеют приоритет над более широкими allowlists. |
| Provider credentials перестали работать. | Переподключите provider, если token истёк и refresh token недоступен, или проверьте, что encryption key всё ещё соответствует сохранённым records. |