# Model providers
Source: https://docs.hyperterse.com/agents/model-providers
Configure Gemini, Vertex AI, and OpenAI-compatible providers for declarative agents.
Hyperterse resolves model providers per agent from `model.provider`, `model.model`, and `model.options`.
## Provider matrix
| Provider value | Backend |
| ----------------------------- | ---------------------------------------------- |
| `gemini`, `google_ai_studio` | Gemini via Google AI Studio |
| `vertex`, `vertex_ai` | Gemini via Vertex AI |
| `openai_compatible`, `openai` | OpenAI-compatible `/chat/completions` endpoint |
When an agent loads, Hyperterse normalizes provider values (lowercased; `-` becomes `_`).
## Option keys and env behavior
| Key | Used by | Behavior / fallback |
| ---------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `api_key` | Gemini, Vertex, OpenAI-compatible | Inline key value. Supports `{{ env.VAR_NAME }}` substitution. If unset, Hyperterse falls back to each provider's default env var. |
| `base_url` | OpenAI-compatible | Base URL; defaults to `https://api.openai.com/v1` when unset. |
| `project` | Vertex | Vertex project id; fallback `GOOGLE_CLOUD_PROJECT`. |
| `location` | Vertex | Vertex location; fallback `GOOGLE_CLOUD_LOCATION`, then `GOOGLE_CLOUD_REGION`. |
Model option string values support `{{ env.VAR_NAME }}` substitution when the agent model is initialized.
## Gemini (Google AI Studio)
```yaml theme={null}
model:
provider: gemini
model: gemini-2.5-flash
```
Supported auth options:
* `api_key` (inline or `{{ env.VAR_NAME }}` substitution)
* fallback env: `GOOGLE_API_KEY` (recommended)
## Vertex AI
```yaml theme={null}
model:
provider: vertex_ai
model: gemini-2.5-pro
options:
project: my-gcp-project
location: us-central1
```
Resolution behavior:
* `project` option or env `GOOGLE_CLOUD_PROJECT`
* `location` option or env `GOOGLE_CLOUD_LOCATION`, fallback `GOOGLE_CLOUD_REGION`
* optional `api_key` support, fallback `GOOGLE_API_KEY`
## OpenAI-compatible
```yaml theme={null}
model:
provider: openai_compatible
model: gpt-4o-mini
options:
base_url: https://api.openai.com/v1
api_key: '{{ env.OPENAI_API_KEY }}'
```
Resolution behavior:
* `base_url` defaults to `https://api.openai.com/v1`
* key from `api_key`, fallback `OPENAI_API_KEY`
## Other popular providers
Direct means you can use `openai_compatible` with that provider's
OpenAI-compatible endpoint directly. Indirect means you should put a compatibility
gateway in front first.
For production, run a quick smoke test with your exact model and one tool call
before rollout.
| Provider | Status |
| --------------- | ----------------------: |
| OpenAI Platform | Direct |
| OpenRouter | Direct |
| Together AI | Direct |
| Groq | Direct |
| Fireworks AI | Direct |
| DeepSeek | Direct |
| Mistral | Direct |
| Perplexity | Direct |
| xAI | Direct |
| LiteLLM gateway | Direct |
| Azure OpenAI | Indirect |
| Anthropic | Indirect |
| Gemini / Vertex | Indirect |
### OpenAI compatibility
`openai_compatible` is defined by protocol behavior, not by vendor name.
#### Accepted provider identifiers
After normalization (lowercase, with `-` converted to `_`), Hyperterse accepts:
* `openai_compatible` (canonical)
* `openai` (alias)
#### Required protocol contract
For direct integration, the target endpoint must support:
* `POST {base_url}/chat/completions`
* `Authorization: Bearer `
* OpenAI-compatible request/response payloads
* OpenAI-compatible tool-call payloads when tools are enabled
#### Direct vs indirect usage
Use `openai_compatible` directly only when the contract above is satisfied.
Use a compatibility gateway when any of these differ:
* endpoint path
* auth mechanism (for example non-Bearer or required custom headers)
* payload/response/tool-call schema
## Production-safe examples
### Gemini with env-managed key
```yaml theme={null}
model:
provider: gemini
model: gemini-2.5-flash
```
### Vertex with explicit project/location
```yaml theme={null}
model:
provider: vertex_ai
model: gemini-2.5-pro
options:
project: my-gcp-project
location: us-central1
```
### OpenAI-compatible with custom gateway
```yaml theme={null}
model:
provider: openai_compatible
model: gpt-4o-mini
options:
base_url: https://my-gateway.example.com/v1
```
### OpenRouter with a free model
```yaml theme={null}
model:
provider: openai_compatible
model: openai/gpt-oss-20b:free
options:
base_url: https://openrouter.ai/api/v1
api_key: '{{ env.OPENROUTER_API_KEY }}'
```
## Recommended patterns
* Keep secrets in provider default env vars (`OPENAI_API_KEY`, `GOOGLE_API_KEY`) instead of inline literals.
* Pin `model` values deliberately per agent role (cheap vs reasoning-heavy).
* Add one agent per workflow role rather than reusing a single all-purpose prompt.
Avoid committing inline `api_key` values to source control. Prefer provider
default env vars and managed secret injection in deployment.
For full agent shape, see [Agent configuration reference](/reference/agent-config) and [Quickstart](/agents/quickstart).
# Overview
Source: https://docs.hyperterse.com/agents/overview
How declarative agents fit into Hyperterse and how to navigate the agent guides.
Hyperterse agents are declarative A2A workloads you define next to tools. You describe behavior in config; Hyperterse validates it and serves each agent on its own HTTP prefix—no separate agent service required.
## What you get
* Declarative setup — agents are config-first, like tools
* Up-front permission checks for which tools an agent may call
* Per-agent HTTP — agent card, JSON-RPC, streaming, tasks, and push settings
* Multi-provider models — Gemini, Vertex AI, and OpenAI-compatible APIs
## How the system fits together
Tools and agents live in the same project. When you build or start the server, Hyperterse loads both, applies tool-access policy, and exposes MCP for tool-style calls and per-agent routes when you want conversational or task-style behavior.
Rough flow:
1. Hyperterse discovers tools and agents from your project.
2. Tool access policy is resolved (`inherit` / `allow_*`).
3. Agent definitions are ready to serve.
4. A2A endpoints are available under `/agent/{agentName}`.
## Endpoint shape per agent
Each agent gets:
* `GET /agent/{agentName}/.well-known/agent-card.json`
* `POST /agent/{agentName}`
Agent routes are separate from MCP `/mcp`. Use MCP when the client is
tool-centric; use `/agent/*` when you need agent protocol behavior.
## Reading path
1. [Quickstart](/agents/quickstart) — create and run your first agent.
2. [Tool access](/agents/tool-access) — lock down permissions correctly.
3. [Runtime API](/agents/runtime-api) — A2A methods and response shapes.
4. [Model providers](/agents/model-providers) — provider-specific setup.
## References
* [Agent config reference](/reference/agent-config)
* [OpenAI compatibility](/agents/model-providers#openai-compatibility)
* [Configuration schemas](/reference/configuration-schemas)
* [Project structure](/concepts/project-structure)
# Quickstart
Source: https://docs.hyperterse.com/agents/quickstart
Create your first declarative agent, run it, and stream responses.
This guide sets up one A2A agent with explicit tool permissions, then exercises
both non-streaming and streaming execution.
## Prerequisites
* A working Hyperterse project
* Hyperterse CLI installed
* A model API key in env vars (for this guide, `OPENAI_API_KEY`)
## Configure root defaults
Add agent discovery + conservative tool defaults in `.hyperterse`:
```yaml .hyperterse theme={null}
name: my-service
agents:
directory: agents
tool_access:
mode: allow_none
```
`allow_none` is a good default: agents must opt in to tools explicitly.
## Define a tool the agent can use
```yaml app/tools/get-orders/config.terse theme={null}
description: "Get orders by status"
use: primary-db
statement: |
SELECT id, status, created_at
FROM orders
WHERE status = {{ inputs.status }}
inputs:
status:
type: string
```
## Define an agent
Create `app/agents/support/config.terse`:
```yaml theme={null}
name: support
description: "Support assistant"
instruction: "Help users with support requests and call tools when useful."
model:
provider: openai_compatible
model: gpt-4o-mini
options:
base_url: "https://api.openai.com/v1"
tool_access:
mode: allow_list
tools:
- get-orders
```
If you are unsure what `openai_compatible` covers, see
[OpenAI compatibility](/agents/model-providers#openai-compatibility).
## Start runtime
```bash theme={null}
hyperterse start
```
## Verify the route is mounted
```bash theme={null}
curl -s http://localhost:8080/agent/support/.well-known/agent-card.json | jq
```
You should see an A2A agent card for `support`.
## Execute non-streaming request
Send a v1 A2A JSON-RPC request:
```bash theme={null}
curl -s -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Find pending orders" }]
}
}
}' | jq
```
## Execute streaming request (SSE)
```bash theme={null}
curl -N -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "SendStreamingMessage",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Summarize pending orders in 3 bullets" }]
}
}
}'
```
## Get a task later
If `SendMessage` returns a task-shaped result, you can retrieve it later:
```bash theme={null}
curl -s -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "GetTask",
"params": { "id": "" }
}' | jq
```
## Common issues
If your model call fails, verify provider credentials (`OPENAI_API_KEY`,
`GOOGLE_API_KEY`, or Vertex env vars) are present.
If tool invocation is unexpectedly blocked, verify the agent allowlist and
root `agents.tool_access` defaults in `.hyperterse`.
Next:
* [Tool access](/agents/tool-access)
* [Runtime API](/agents/runtime-api)
* [Model providers](/agents/model-providers)
# Runtime API
Source: https://docs.hyperterse.com/agents/runtime-api
Use Hyperterse A2A v1 endpoints mounted per agent.
For how A2A fits next to MCP at runtime, see [A2A transport](/runtime/a2a-transport).
Each agent is exposed behind its own route prefix:
```text theme={null}
/agent/{agentName}
```
So a `support` agent gets:
* `GET /agent/support/.well-known/agent-card.json`
* `POST /agent/support`
## What the endpoint serves
The A2A mount provides a stateful agent contract over one JSON-RPC endpoint:
* agent discovery via the public card
* request/response execution
* SSE streaming
* task retrieval, cancellation, and resubscription
* push-notification config operations
## Endpoint reference
| Endpoint | Method(s) | Purpose |
| ------------------------------------------------ | --------- | --------------------------------- |
| `/agent/{agentName}/.well-known/agent-card.json` | `GET` | Return the public A2A agent card. |
| `/agent/{agentName}` | `POST` | Serve A2A v1 JSON-RPC methods. |
## Supported JSON-RPC methods
* `SendMessage`
* `SendStreamingMessage`
* `GetTask`
* `ListTasks`
* `CancelTask`
* `SubscribeToTask`
* `GetTaskPushNotificationConfig`
* `ListTaskPushNotificationConfigs`
* `CreateTaskPushNotificationConfig`
* `DeleteTaskPushNotificationConfig`
* `GetExtendedAgentCard`
## Request body contract
`SendMessage` and `SendStreamingMessage` use this shape:
```json theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Find pending orders" }]
}
}
}
```
Streaming uses the same payload but changes `method` to
`SendStreamingMessage`.
## Typical flow (recommended)
1. `GET /agent/{agentName}/.well-known/agent-card.json`
2. `POST /agent/{agentName}` with `SendMessage` or `SendStreamingMessage`
3. If you receive a task result, use `GetTask` for later polling
4. Use `SubscribeToTask` for SSE replay of task events
Continuity is task-based: use task IDs to poll, stream, or resume work across
turns.
## Request examples
### SendMessage
```bash theme={null}
curl -s -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Find all pending orders from today" }]
}
}
}' | jq
```
### SendStreamingMessage
```bash theme={null}
curl -N -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "SendStreamingMessage",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Summarize pending orders in 3 bullets" }]
}
}
}'
```
### GetTask
```bash theme={null}
curl -s -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "GetTask",
"params": { "id": "" }
}' | jq
```
### SubscribeToTask
```bash theme={null}
curl -N -X POST http://localhost:8080/agent/support \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "SubscribeToTask",
"params": { "id": "" }
}'
```
## Operational notes
* `SendMessage` can return a task-shaped result when execution is stateful.
* Tool permissions follow the allowlist in each agent's config.
* On model reload, agent routes match your latest declarations.
* CORS headers are set on agent routes for browser-friendly access.
## Troubleshooting
If tool invocation is unexpectedly blocked, check the agent's `tool_access`
policy and root defaults in `.hyperterse`.
Next: configure providers in [Model providers](/agents/model-providers) and tune permission policy in [Tool access](/agents/tool-access).
# Tool access
Source: https://docs.hyperterse.com/agents/tool-access
Practical playbooks for choosing and rolling out agent tool permissions.
This page focuses on how to choose and roll out tool permissions for agents safely.
For exact field definitions, schema constraints, and every option, use
[Agent configuration reference](/reference/agent-config).
## Why this matters
Tool permissions are where most production incidents happen:
* agents can call more tools than intended
* broad permissions can hide prompt mistakes
* late permission validation can break deployments
Hyperterse helps by resolving effective permissions at compile time, but you still need
good policy design.
## Use one of these playbooks
### Playbook A: safest default (recommended)
Use this when you're introducing agents into an existing system.
```yaml .hyperterse theme={null}
agents:
tool_access:
mode: allow_none
```
Then grant access agent-by-agent:
```yaml app/agents/refunds/config.terse theme={null}
name: refunds
instruction: "Handle refund requests."
model:
provider: openai_compatible
model: gpt-4o-mini
tool_access:
mode: allow_list
tools:
- get-order-status
- issue-refund
```
Why this works:
* no accidental broad access
* explicit change review when a new tool is added
* easy audit trail of agent capabilities
### Playbook B: shared baseline for many agents
Use this when multiple agents need the same small capability set.
```yaml .hyperterse theme={null}
agents:
tool_access:
mode: allow_list
tools:
- get-order-status
- search-orders
```
Agents inherit the baseline by default (`tool_access` can be omitted entirely):
```yaml app/agents/support/config.terse theme={null}
name: support
instruction: "Answer support requests."
model:
provider: openai_compatible
model: gpt-4o-mini
```
This is equivalent to explicitly setting `tool_access.mode: inherit`.
Override only when needed:
```yaml app/agents/disputes/config.terse theme={null}
name: disputes
instruction: "Handle dispute workflows."
model:
provider: openai_compatible
model: gpt-4o-mini
tool_access:
mode: allow_list
tools:
- get-order-status
- create-dispute
```
### Playbook C: broad access for internal-only agents
Use this only for controlled internal workflows.
```yaml app/agents/internal-ops/config.terse theme={null}
name: internal-ops
instruction: "Support internal operations."
model:
provider: vertex_ai
model: gemini-2.5-pro
tool_access:
mode: allow_all
```
`allow_all` is convenient but high risk. Prefer explicit allowlists in production.
## Rollout checklist
Before enabling an agent in production:
1. Start from `allow_none` at root or a minimal shared allowlist.
2. Grant only task-specific tools per agent.
3. Verify no hidden dependency on undeclared tools.
4. Test one successful and one denied tool path.
5. Review changes to allowlists in code review.
## Common mistakes
### Mistake: using `allow_all` early
This often hides prompt/tool routing problems until late.
### Mistake: setting `allow_list` but forgetting one tool
The build catches unknown tools and bad lists, but live behavior is still easier to reason about with tests.
### Mistake: treating this page as schema docs
This is intentionally guide-oriented. Use [Agent configuration reference](/reference/agent-config)
for field-level specification.
## Quick troubleshooting
If a tool call is blocked unexpectedly:
* check effective policy in the agent config (`inherit` vs override)
* check root defaults in `.hyperterse`
* confirm tool name matches discovered tool folder/name
## Next steps
* [Quickstart](/agents/quickstart)
* [Runtime API](/agents/runtime-api)
* [Agent configuration reference](/reference/agent-config)
# Adapters
Source: https://docs.hyperterse.com/concepts/adapters
Named database connections that tools reference for query execution.
An adapter is a named binding between Hyperterse and your database. You declare the connector and connection string in an adapter config file; tools point at adapters by name with the `use` field. The framework handles pooling, health checks, and graceful shutdown.
## Defining an adapter
Each adapter file declares a connector type and a connection string:
```yaml theme={null}
name: primary-db
connector: postgres
connection_string: '{{ env.DATABASE_URL }}'
options:
sslmode: require
```
The `connector` field selects the database engine. The `connection_string` accepts `{{ env.VAR }}` placeholders, which Hyperterse resolves from environment variables when the server starts. Never commit plaintext credentials — always use environment variables or a secrets manager.
## Supported connectors
Hyperterse ships with many built-in connectors:
SQL queries via `postgresql://` connection strings.
SQL queries via DSN-format connection strings.
Local SQLite files and remote libSQL/Turso over HTTP.
JSON command payloads via `mongodb://` connection strings.
Key-value operations via `redis://` connection strings.
## Referencing adapters from tools
Tools point to an adapter by name:
```yaml theme={null}
# excerpt from a tool definition
use: primary-db
statement: 'SELECT * FROM users WHERE id = {{ inputs.user_id }}'
```
A project can define multiple adapters, and different tools can reference different ones:
## Lifecycle
Every adapter your tools use starts in parallel when the server boots. Each connector opens its pool and checks connectivity. If any check fails (bad host, credentials, timeout), Hyperterse exits so you do not serve tools that would fail every call.
On shutdown (`SIGINT` / `SIGTERM`), all connectors close concurrently after in-flight queries complete.
## Further reading
See [Adapter configuration reference](/reference/adapter-config) for the complete field specification, including driver-specific options for each connector.
# Authentication
Source: https://docs.hyperterse.com/concepts/authentication
Tool-level authentication with built-in and custom plugins.
Authentication in Hyperterse is tool-scoped and plugin-based. Each tool declares which plugin to use and supplies policy parameters. Auth runs after the tool is resolved and before input transforms or execution.
Tools without an `auth` block are unauthenticated. There is no global auth middleware.
## Configuration
Add an `auth` block to any tool config:
```yaml theme={null}
auth:
plugin: api_key
policy:
value: '{{ env.API_KEY }}'
```
The `plugin` field selects the auth strategy. The `policy` map passes plugin-specific parameters — typically credentials or validation rules.
## Built-in plugins
Hyperterse ships with two plugins that cover the most common access patterns:
The `allow_all` plugin unconditionally allows every request. Use for health checks, public tools, or development.
```yaml theme={null}
auth:
plugin: allow_all
```
The `api_key` plugin validates the `X-API-Key` HTTP header against a configured value. The expected key is resolved from `policy.value` (supports `{{ env.VAR }}` substitution) or falls back to the `HYPERTERSE_API_KEY` environment variable.
```yaml theme={null}
auth:
plugin: api_key
policy:
value: '{{ env.MY_SECRET_KEY }}'
```
## Auth flow
When a tool runs, Hyperterse checks for an `auth` block on that tool. If you configured one, the plugin runs with the request context and policy map. Success continues the request; failure stops immediately with an authentication error.
## Custom plugins
You can register custom auth plugins to implement strategies like JWT validation, OAuth bearer tokens, or IP allowlisting. Once registered, use the plugin name in tool configs just like the built-in ones:
```yaml theme={null}
auth:
plugin: jwt_bearer
policy:
issuer: 'https://auth.example.com'
audience: 'my-service'
```
A plugin receives the request headers (extracted from the HTTP transport) and the policy map from the tool config. Return success to authorize, or an error to reject.
## Key points
* Auth is per-tool. Every tool that needs protection must declare it. There is no implicit inheritance.
* No `auth` block means no authentication. The tool is accessible to anyone who can reach the endpoint.
* Use environment variables for secrets. `{{ env.VAR }}` in policy values keeps credentials out of config files.
* `allow_all` is not a security boundary. It exists for convenience — do not use it on production tools that access sensitive data.
# Project structure
Source: https://docs.hyperterse.com/concepts/project-structure
Filesystem conventions and directory layout for Hyperterse projects.
Hyperterse uses the filesystem as the source of truth. You define database connections, tools, prompts, and resources in role-based directories. File and directory names become identifiers. Hyperterse discovers everything from your project layout—you do not register tools or agents in code.
## Directory layout
A typical project looks like this:
## Root configuration
The `.hyperterse` file contains service-level settings: name, version, server port, log level, cache defaults, build options, and discovery settings for adapters, tools, prompts, resources, and agents. Adapter, tool, prompt, resource, and agent definitions live under `app/` (or inline for prompts/resources via root arrays). Inline `prompts`, `resources`, and `resource_templates` arrays are optional alternatives to filesystem discovery.
When you run `hyperterse start` or `hyperterse build` without arguments, the CLI looks for `.hyperterse` in the current directory. See [Root configuration reference](/reference/root-config) for the complete field specification.
## Adapter discovery
When you build or start the server, Hyperterse loads adapter definitions from `app/adapters/*.terse`. Each file defines one adapter. The filename (without extension) becomes the adapter's identifier, unless the file sets a `name` field.
See [Adapters](/concepts/adapters) for how adapters work, and [Adapter configuration reference](/reference/adapter-config) for the field specification.
## Tool discovery
Hyperterse discovers tools from `app/tools/*/config.terse` when you build or start. Each `config.terse` defines one MCP tool. The directory name becomes the tool name unless the file sets `name`.
Every tool must define either a database connection (`use`) or a handler script (`handler`). Tools with neither are rejected during validation.
See [Tools](/concepts/tools) for execution models, and [Tool configuration reference](/reference/tool-config) for the field specification.
## Prompt discovery
Prompt definitions come from `app/prompts/**/*.terse`. Each file is one MCP prompt. The filename is the prompt name unless you set `name`.
See [Prompts](/concepts/prompts) and [Prompt configuration reference](/reference/prompt-config).
## Resource discovery
Resources are defined under `app/resources/**/config.terse`. Each folder is one concrete resource (`uri`) or one template (`uri_template`), with optional content files alongside.
See [Resources](/concepts/resources) and [Resource configuration reference](/reference/resource-config).
## Agent discovery
Agent definitions live in `app/agents/*/config.terse`. Each file is one declarative agent served over A2A. The directory name is the agent name unless you set `name`.
Agent files define:
* instruction and model provider config
* tool access policy (`inherit`, `allow_all`, `allow_none`, `allow_list`)
See [Overview](/agents/overview) and [Agent configuration reference](/reference/agent-config).
## Script resolution
Script paths in tool configs are resolved relative to the tool directory. You can declare them explicitly, or rely on convention-based discovery. Common conventions include `handler.ts`, `input.ts`, and `output.ts`.
See [Scripts](/concepts/scripts) for the full script model.
## Vendor dependencies
If your tool scripts import npm packages, add a `package.json` at the project root and install your dependencies. Build output includes everything needed to run scripts, so deployment does not depend on `node_modules/` being present.
# Overview
Source: https://docs.hyperterse.com/concepts/prompts
Reusable MCP prompts with arguments, interpolation, and completion hints.
Prompts let you package reusable conversation scaffolding directly in your Hyperterse project. At runtime, these definitions are exposed through MCP `prompts/list` and `prompts/get`, so clients can discover prompt templates and instantiate them with concrete arguments.
## Where prompt definitions live
Prompts are ordinary `.terse` files in your project tree. Default folder layout and discovery rules are documented in [Project structure](/concepts/project-structure)—use that page when you need to know exactly where files go.
You can point prompt discovery at a different directory from `.hyperterse`:
```yaml .hyperterse theme={null}
prompts:
directory: prompts
```
You can also define prompts inline in `.hyperterse` using the `prompts` array form. See [Root configuration](/reference/root-config).
## Prompt file shape
Each prompt file defines:
* prompt identity (`name`, optional; defaults to filename)
* optional metadata (`title`, `description`)
* optional argument definitions (`arguments`)
* one or more message templates (`messages`)
```yaml theme={null}
name: summarize-release
title: Release summary helper
description: Generate a concise release summary from notes.
arguments:
audience:
description: Intended audience for the summary
required: true
completion: ['engineering', 'product', 'customers']
tone:
description: Writing tone
completion: ['concise', 'detailed']
messages:
- role: system
text: You summarize software releases for {{ audience }}.
- role: user
text: Write a {{ tone }} summary for this release.
```
## Argument interpolation
When a client calls `prompts/get`, Hyperterse interpolates `{{ argumentName }}` placeholders in each message using the provided prompt arguments.
* Missing placeholders are left as-is.
* Supported placeholder token characters are alphanumeric and underscore.
* Message order is preserved exactly as configured.
## Runtime behavior
Once loaded, prompt definitions are available through MCP:
* `prompts/list` to discover prompts
* `prompts/get` to render message content with arguments
* `completion/complete` for prompt argument completions when `completion` values are declared
* `notifications/prompts/list_changed` when prompt definitions change after a model reload
## Validation and constraints
Prompt configs are validated before runtime:
* prompt names must be unique
* at least one `messages` entry is required
* each message requires `role` + `text`
* supported roles: `user`, `assistant`, `system`
* argument names must be unique within a prompt
See [Prompt configuration reference](/reference/prompt-config) for full field details.
# Overview
Source: https://docs.hyperterse.com/concepts/resources
MCP resources and URI templates—inline content or files.
Resources expose structured read-only context through MCP `resources/*` APIs. Hyperterse supports both:
* concrete resources with fixed `uri`
* resource templates with parameterized `uri_template`
## Where resource definitions live
Resources use a `config.terse` per logical item, often with sibling content files. Default layout and discovery are covered in [Project structure](/concepts/project-structure).
Each resource typically has its own folder containing:
* `config.terse` (required)
* optional resource content files (for example `release-notes.md`, `1001.json`, images, or other formats)
You can override discovery in `.hyperterse`:
```yaml .hyperterse theme={null}
resources:
directory: resources
```
You can also define concrete resources (`resources`) and templates (`resource_templates`) inline in `.hyperterse`. See [Root configuration](/reference/root-config).
## Concrete resources
Concrete resources define a fixed URI and one content source:
* inline `text`, or
* file-backed `file`
```yaml theme={null}
uri: memory://release-notes/latest
name: release-notes
mime_type: text/markdown
file: ./release-notes.md
```
## Resource templates
Templates define parameterized URIs and template-based content:
* inline `text_template`, or
* file-backed `file_template`
```yaml theme={null}
uri_template: memory://orders/{id}
name: order-details
mime_type: application/json
text_template: '{"orderId":"{{ id }}","status":"pending"}'
arguments:
id:
description: Order identifier
required: true
completion: ['1001', '1002', '1003']
```
## Runtime behavior
Loaded resource definitions are exposed through MCP:
* `resources/list` for concrete resources
* `resources/templates/list` for template resources
* `resources/read` for content retrieval
* `resources/subscribe` / `resources/unsubscribe` for update subscriptions
* `completion/complete` for template argument completions (when configured)
* `notifications/resources/list_changed` when resource definitions change
* `notifications/resources/updated` when a concrete resource changes on reload
## Content and MIME handling
Hyperterse resolves resource content using these rules:
1. Use configured `mime_type` if present.
2. Otherwise infer from file extension (for file-backed content).
3. If no MIME can be inferred:
* text data defaults to `text/plain; charset=utf-8`
* binary data defaults to `application/octet-stream`
File-backed resources return text when content is UTF-8/text; otherwise they return binary blob content.
## File paths and safety
* Relative `file` and `file_template` paths are resolved relative to the resource `config.terse` directory.
* For `file_template`, path traversal patterns (`..`) are rejected after interpolation.
## Validation and constraints
Resource configs are validated before runtime:
* exactly one of `uri` or `uri_template` must be set
* concrete resources require one of `text` or `file`
* template resources require one of `text_template` or `file_template`
* resource URIs must be unique across concrete resources
* URI templates must be unique across template resources
See [Resource configuration reference](/reference/resource-config) for full field details.
# Scripts
Source: https://docs.hyperterse.com/concepts/scripts
TypeScript handlers and transforms that extend tool behavior beyond configuration.
Scripts extend tool behavior beyond what configuration can express. A handler script replaces database execution entirely. Transform scripts modify inputs before execution and results after. All three are TypeScript: Hyperterse includes them when you build, then runs them in a sandboxed environment without extra runtimes on the host.
## Script hooks
There are three hooks, each corresponding to a stage in the execution pipeline:
| Hook | Default export | Purpose |
| ---------------- | ---------------------- | -------------------------------------- |
| Handler | `export default (...)` | Replaces DB execution entirely |
| Input transform | `export default (...)` | Pre-process arguments before execution |
| Output transform | `export default (...)` | Post-process results before returning |
## Selecting exported functions
For all script references, you can target a non-default export with `#`:
```yaml theme={null}
handler: "./weather-handler.ts#weather"
mappers:
input: "./input.ts#normalize"
output: "./output.ts#shape"
```
Without `#exportName`, Hyperterse calls the script's `export default` function.\
The function name itself is optional; Hyperterse does not use it.
## Handler
A handler replaces adapter-based execution. When `handler` is configured, no `use` or `statement` is needed. The return value becomes the tool's result payload.
```typescript theme={null}
export default function handler(payload: {
inputs: Record
tool: string
}) {
const { inputs } = payload
return {
location: inputs.city || 'unknown',
temperature_c: 22.5,
conditions: 'partly cloudy',
}
}
```
## Input transform
An input transform pre-processes arguments before they reach the executor or handler. Use it for validation, normalization, or rejection. The returned object replaces the original inputs for all subsequent pipeline stages. Throwing an error aborts execution.
```typescript theme={null}
export default function inputTransform(payload: {
inputs: Record
tool: string
}) {
if (
typeof payload.inputs.user_id !== 'number' ||
payload.inputs.user_id <= 0
) {
throw new Error('user_id must be a positive integer')
}
return { ...payload.inputs, user_id: Math.floor(payload.inputs.user_id) }
}
```
## Output transform
An output transform post-processes results before they are returned. Use it for field mapping, formatting, or redaction.
```typescript theme={null}
export default function outputTransform(payload: { results: any[]; tool: string }) {
return payload.results.map((row) => ({
id: row.id,
name: row.name,
created_at_iso: new Date(row.created_at).toISOString(),
}))
}
```
## Runtime APIs
Scripts execute in a sandboxed runtime with two injected globals:
* `fetch(url, options?)` — HTTP client for outbound requests. Returns `{ status, ok, text(), json() }`.
* `console` — `log`, `error`, `warn`, `info`, `debug` — wired to the structured logger with the tool name as context.
Scripts cannot access the host filesystem, spawn processes, or open network sockets. `setTimeout` and `setInterval` are not available — use `async`/`await` instead.
## Convention discovery
When script paths are omitted in tool config, Hyperterse auto-discovers script files
from the tool directory. Convention names include:
* `handler.ts`
* `input.ts` (or `*input*validator*.ts`)
* `output.ts` (or `*data*mapper*.ts`)
## npm packages
If your scripts import external packages, add a `package.json` at the project root. The build process packages dependencies with your scripts, so deployment does not require `node_modules/`.
```typescript theme={null}
import dayjs from 'dayjs'
import { v4 as uuidv4 } from 'uuid'
```
## Error handling
Errors thrown from scripts propagate as MCP error responses. The error message is included in the response; stack traces are logged at debug level but not exposed to callers.
## Further reading
See [Execution pipeline](/runtime/execution-pipeline) for how scripts fit into the request lifecycle, and [Tool configuration reference](/reference/tool-config) for `handler` and `mappers` configuration.
# Tools
Source: https://docs.hyperterse.com/concepts/tools
How tool definitions specify MCP tools, input schemas, and execution strategies.
A tool definition is the central abstraction in Hyperterse. Each tool config file defines exactly one MCP tool: name, description, how it runs, inputs, authentication, and caching. On startup, every valid definition appears in the MCP `tools/list` response.
## Execution models
A tool definition's execution model is determined by its configuration. There are two primary models.
DB-backed tools execute a SQL or database command through a connector. They require `use` (the adapter name) and `statement`:
```yaml theme={null}
description: 'Retrieve a user by their identifier'
use: primary-db
statement: |
SELECT id, name, email, created_at
FROM users
WHERE id = {{ inputs.user_id }}
inputs:
user_id:
type: int
description: 'Primary key of the user record'
auth:
plugin: allow_all
```
Script-backed tools delegate execution to a TypeScript handler. No adapter or statement is needed:
```yaml theme={null}
description: 'Retrieve current weather conditions for a location'
handler: './weather-handler.ts'
auth:
plugin: allow_all
```
Hybrid tools combine a DB adapter with mappers for pre-processing and post-processing.
Execution order is: auth, input mapper, DB execution, output mapper.
## Tool naming
The MCP tool name comes from the tool definition. If the config sets a `name` field, that value wins. Otherwise the folder name becomes the tool name (for example a folder named `get-user` yields the tool `get-user`). Names must be unique—Hyperterse rejects duplicates when you build or start.
## Input schema
The `inputs` block defines typed parameters that the tool accepts.
```yaml theme={null}
inputs:
user_id:
type: int
description: 'Primary key of the user'
include_inactive:
type: boolean
description: 'Whether to include deactivated accounts'
optional: true
default: 'false'
```
Supported types are `string`, `int`, `float`, `boolean`, and `datetime`. Required inputs without a default must be provided by the caller. References in statements use `{{ inputs.field_name }}` placeholders.
## Caching
Tools can override the global cache policy with a `cache` block. See [Caching](/runtime/caching) for the full cache model.
## Further reading
See [Tool configuration reference](/reference/tool-config) for the complete field specification, including input properties, mapper options, and cache overrides.
# MongoDB
Source: https://docs.hyperterse.com/databases/mongodb
Connect Hyperterse to MongoDB for document-backed MCP tools using native database commands.
Hyperterse supports MongoDB as a document database connector. Statements are passed as native MongoDB database commands — the same syntax used by `db.runCommand()` in the MongoDB shell. This gives you access to any operation MongoDB supports: `find`, `aggregate`, `insert`, `update`, `delete`, and administrative commands.
Hyperterse connects to your existing database. It does not create or manage
databases — you provide a running MongoDB instance (self-hosted or [MongoDB
Atlas](https://www.mongodb.com/atlas)) and a connection string.
## Adapter configuration
Create an adapter file in `app/adapters/`:
```yaml app/adapters/mongo-db.terse theme={null}
connector: mongodb
connection_string: '{{ env.MONGODB_URL }}'
options:
maxPoolSize: '50'
minPoolSize: '5'
```
The connection string uses the standard MongoDB URI format:
`mongodb://user:password@host:27017/database`
`mongodb+srv://user:password@cluster.mongodb.net/database`
### Connection options
Maximum number of connections in the pool.
Minimum number of idle connections maintained in the pool.
Timeout in milliseconds for establishing a new connection.
Timeout in milliseconds for selecting a server from a replica set or sharded
cluster.
### Verify the connection
Start the server and confirm the adapter connects:
```bash theme={null}
hyperterse start
```
A successful connection produces:
```
INFO Connected to adapter: mongo-db
```
If the connection fails, the server exits immediately with a diagnostic message.
## Usage
MongoDB tool statements are JSON objects with two required fields: `database` and `command`. The `command` field takes a raw MongoDB database command.
```yaml app/tools/find-users/config.terse theme={null}
description: 'Find users by name'
use: mongo-db
statement: |
{
"database": "mydb",
"command": {
"find": "users",
"filter": { "name": "{{ inputs.name }}" },
"limit": 10
}
}
inputs:
name:
type: string
description: 'User name to search for'
auth:
plugin: allow_all
```
The command name (e.g., `"find"`, `"insert"`, `"aggregate"`) must be the
first key inside the `command` object. MongoDB rejects commands where the
operation name is not the first field.
### Statement format
| Field | Description |
| ---------- | ------------------------------------------------------------------------------------------------ |
| `database` | The MongoDB database to run the command against. |
| `command` | A raw MongoDB [database command](https://www.mongodb.com/docs/manual/reference/command/) object. |
### Aggregation example
```yaml app/tools/sales-summary/config.terse theme={null}
description: 'Aggregate sales by product category'
use: mongo-db
statement: |
{
"database": "analytics",
"command": {
"aggregate": "sales",
"pipeline": [
{ "$match": { "year": {{ inputs.year }} } },
{ "$group": { "_id": "$category", "total": { "$sum": "$amount" } } },
{ "$sort": { "total": -1 } }
],
"cursor": {}
}
}
inputs:
year:
type: int
description: 'Calendar year to aggregate'
auth:
plugin: allow_all
```
### ObjectId handling
For queries involving ObjectId fields, pass them using the extended JSON format:
```json theme={null}
{ "filter": { "_id": { "$oid": "{{ inputs.id }}" } } }
```
## Troubleshooting
### Connection refused
Verify MongoDB is running and reachable:
```bash theme={null}
mongosh "mongodb://localhost:27017"
```
Check firewall rules for remote instances and ensure the Atlas IP allowlist includes your server's address.
### Authentication failed
Ensure the connection string contains the correct username and password. URL-encode special characters in passwords (e.g., `@` becomes `%40`).
### TLS / Atlas
For MongoDB Atlas, always use the `mongodb+srv://` URI provided by Atlas. The driver handles TLS and server discovery automatically. For custom certificates, configure TLS options in the connection string.
### Invalid statement JSON
The `statement` field must be valid JSON after `{{ inputs.* }}` substitution. Ensure all placeholders resolve to properly quoted JSON values. The `command` object must have the operation name as its first key.
# MySQL
Source: https://docs.hyperterse.com/databases/mysql
Connect Hyperterse to MySQL for SQL-backed MCP tools with connection pooling and charset configuration.
Hyperterse supports MySQL with connection pooling, character set configuration, and parameterized query execution. All standard SQL features — joins, aggregations, subqueries, JSON functions, and window functions — work as expected in tool statements.
Hyperterse connects to your existing database. It does not create or manage
databases — you provide a running MySQL instance and a connection string.
## Adapter configuration
Create an adapter file in `app/adapters/`:
```yaml app/adapters/mysql-db.terse theme={null}
connector: mysql
connection_string: '{{ env.MYSQL_URL }}'
options:
charset: utf8mb4
max_connections: '10'
```
The connection string uses the MySQL DSN format:
```
user:password@tcp(host:port)/database?param=value
```
MySQL DSN format differs from PostgreSQL URI format. The host and port are
wrapped in `tcp(...)`.
### Connection options
Character set for the connection. Use `utf8mb4` for full Unicode support
including emoji.
Maximum number of connections in the pool.
### Connection string parameters
Fine-tune behavior through query parameters on the connection string:
```yaml theme={null}
connection_string: 'user:pass@tcp(host:3306)/db?charset=utf8mb4&parseTime=true&loc=UTC&timeout=10s'
```
| Parameter | Example | Purpose |
| -------------- | --------- | -------------------------------------------- |
| `charset` | `utf8mb4` | Connection character set |
| `parseTime` | `true` | Parse `DATE` and `DATETIME` into time values |
| `loc` | `UTC` | Timezone for parsed datetimes |
| `timeout` | `10s` | Connection timeout |
| `readTimeout` | `30s` | I/O read timeout |
| `writeTimeout` | `30s` | I/O write timeout |
### Recommended permissions
Create a dedicated database user for Hyperterse and grant only the privileges your tools require:
```sql theme={null}
-- Read-only
GRANT SELECT ON myapp.* TO 'hyperterse'@'%';
-- Read-write
GRANT INSERT, UPDATE, DELETE ON myapp.* TO 'hyperterse'@'%';
-- Specific tables only
GRANT SELECT ON myapp.users, myapp.products TO 'hyperterse'@'%';
```
### Verify the connection
Start the server and confirm the adapter connects:
```bash theme={null}
hyperterse start
```
A successful connection produces:
```
INFO Connected to adapter: mysql-db
```
If the connection fails, the server exits immediately with a diagnostic message.
## Usage
MySQL tools execute standard SQL through the adapter. Use `{{ inputs.field }}` placeholders for parameterized values.
```yaml app/tools/get-product/config.terse theme={null}
description: 'Retrieve a product by its identifier'
use: main-db
statement: |
SELECT id, name, price, description
FROM products
WHERE id = {{ inputs.product_id }}
inputs:
product_id:
type: int
description: 'Product ID'
auth:
plugin: allow_all
```
MySQL-specific features like `JSON_EXTRACT`, stored procedures, and window functions are all supported. Use standard MySQL syntax in statements.
### Read replicas
Configure separate adapters for primary and replica databases:
```yaml app/adapters/primary.terse theme={null}
connector: mysql
connection_string: '{{ env.PRIMARY_DB_URL }}'
```
```yaml app/adapters/replica.terse theme={null}
connector: mysql
connection_string: '{{ env.REPLICA_DB_URL }}'
```
Send read-only tools to the replica adapter.
## Troubleshooting
### Access denied
Verify user grants:
```sql theme={null}
SHOW GRANTS FOR 'hyperterse'@'%';
```
Ensure the user exists with the correct host permissions, then `FLUSH PRIVILEGES`.
### Character set issues
Use `utf8mb4` in the connection string to support the full Unicode range:
```
connection_string: "user:pass@tcp(host:3306)/db?charset=utf8mb4"
```
Verify the database character set:
```sql theme={null}
SHOW CREATE DATABASE myapp;
```
### Connection timeout
Increase timeout values in the connection string:
```
connection_string: "user:pass@tcp(host:3306)/db?timeout=30s&readTimeout=30s&writeTimeout=30s"
```
### Timezone issues
Set the timezone explicitly for consistent datetime handling:
```
connection_string: "user:pass@tcp(host:3306)/db?parseTime=true&loc=UTC"
```
# PostgreSQL
Source: https://docs.hyperterse.com/databases/postgresql
Connect Hyperterse to PostgreSQL for SQL-backed MCP tools with connection pooling and SSL.
PostgreSQL is the most common connector for Hyperterse deployments. It supports all standard SQL features — joins, aggregations, window functions, JSON operations, full-text search, and advanced data types — with connection pooling, SSL/TLS, and parameterized query execution built in.
Hyperterse connects to your existing database. It does not create or manage
databases — you provide a running PostgreSQL instance and a connection string.
## Adapter configuration
Create an adapter file in `app/adapters/`:
```yaml app/adapters/primary-db.terse theme={null}
connector: postgres
connection_string: '{{ env.DATABASE_URL }}'
options:
sslmode: require
connect_timeout: '10'
```
The connection string follows the standard PostgreSQL URI format:
```
postgresql://user:password@host:port/database?param=value
```
Always use `{{ env.VAR }}` placeholders for connection strings. Never commit credentials to source control.
### Connection options
Pass driver-level settings through the `options` map. All values must be strings.
SSL negotiation mode. Use `require` or stricter in production.
Values: `disable`, `allow`, `prefer`, `require`, `verify-ca`, `verify-full`
Connection timeout in seconds. Connections that exceed this threshold are
terminated.
Application name reported to the PostgreSQL server. Useful for identifying
connections in `pg_stat_activity`.
Maximum number of connections in the pool.
Never use `sslmode: disable` with cloud-hosted databases or production
environments. Connection traffic is unencrypted.
### Recommended permissions
Create a dedicated database user for Hyperterse and grant only the privileges your tools require:
```sql theme={null}
-- Read-only access
GRANT SELECT ON ALL TABLES IN SCHEMA public TO hyperterse;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO hyperterse;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO hyperterse;
```
For tools that write data, add the necessary privileges:
```sql theme={null}
GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO hyperterse;
```
### Verify the connection
Start the server and confirm the adapter connects:
```bash theme={null}
hyperterse start
```
A successful connection produces:
```
INFO Connected to adapter: primary-db
```
If the connection fails, the server exits immediately with a diagnostic message.
## Usage
PostgreSQL tools execute standard SQL through the adapter. Use `{{ inputs.field }}` placeholders for parameterized values — they are never interpolated as raw SQL.
```yaml app/tools/get-user/config.terse theme={null}
description: 'Retrieve a user by their identifier'
use: main-db
statement: |
SELECT id, name, email, created_at
FROM users
WHERE id = {{ inputs.user_id }}
inputs:
user_id:
type: int
description: 'Primary key of the user record'
auth:
plugin: allow_all
```
PostgreSQL-specific features like `jsonb` operators, `tsvector` search, array operations, CTEs, and window functions are all supported. Use standard PostgreSQL syntax in statements.
### Read replicas
For read-heavy workloads, configure separate adapters for primary and replica databases:
```yaml app/adapters/primary.terse theme={null}
connector: postgres
connection_string: '{{ env.PRIMARY_DB_URL }}'
```
```yaml app/adapters/replica.terse theme={null}
connector: postgres
connection_string: '{{ env.REPLICA_DB_URL }}'
```
Send read-only tools to the replica adapter and write tools to the primary.
## Troubleshooting
### Connection refused
Verify that PostgreSQL is running and the host is reachable:
```bash theme={null}
psql -h localhost -U hyperterse -d myapp
```
Check firewall rules and security groups if connecting to a remote or cloud-hosted instance.
### SSL certificate errors
For self-signed certificates, set `sslmode` to `require` (encrypts traffic without certificate verification). For full verification, use `verify-ca` or `verify-full` and ensure the CA certificate is installed on the machine running Hyperterse.
### Permission denied
Inspect the current grants for your user:
```sql theme={null}
\dp table_name
```
Grant the missing permissions, then restart Hyperterse to re-establish the connection pool.
# Redis
Source: https://docs.hyperterse.com/databases/redis
Connect Hyperterse to Redis for key-value, caching, and data structure operations.
Hyperterse supports Redis as a connector for key-value operations, caching lookups, session retrieval, rate limit checks, and working with Redis data structures (hashes, lists, sets, sorted sets). Statements are standard Redis commands executed directly against the server.
Hyperterse connects to your existing Redis instance. It does not create or
manage databases — you provide a running Redis server (self-hosted, AWS
ElastiCache, Upstash, etc.) and a connection string.
## Adapter configuration
Create an adapter file in `app/adapters/`:
```yaml app/adapters/cache.terse theme={null}
connector: redis
connection_string: '{{ env.REDIS_URL }}'
options:
pool_size: '10'
```
The connection string uses the standard Redis URI format:
`redis://user:password@host:port/db_number`
`rediss://user:password@host:port/db_number` Note the double `s` in
`rediss://` for TLS-encrypted connections.
### Connection options
Maximum number of connections in the pool.
For password-protected instances, include the password in the URI:
```yaml theme={null}
connection_string: 'redis://:password@localhost:6379/0'
```
For TLS-enabled cloud providers, use the `rediss://` scheme:
```yaml theme={null}
connection_string: 'rediss://:password@host:6379/0'
```
### Verify the connection
Start the server and confirm the adapter connects:
```bash theme={null}
hyperterse start
```
A successful connection produces:
```
INFO Connected to adapter: cache
```
If the connection fails, the server exits immediately with a diagnostic message.
## Usage
Redis tool statements contain the Redis command to execute. Use `{{ inputs.field }}` placeholders for dynamic values.
```yaml app/tools/get-cached-value/config.terse theme={null}
description: 'Retrieve a cached value by key'
use: cache
statement: 'GET {{ inputs.key }}'
inputs:
key:
type: string
description: 'Cache key'
auth:
plugin: allow_all
```
Redis commands return results in JSON format. Multiple values are returned as arrays; hash operations return key-value pairs.
### Common command patterns
```yaml theme={null}
# GET
statement: "GET {{ inputs.key }}"
# SET with expiry
statement: "SET {{ inputs.key }} {{ inputs.value }} EX {{ inputs.ttl }}"
```
```yaml theme={null}
# Get all fields
statement: "HGETALL {{ inputs.key }}"
# Get a specific field
statement: "HGET {{ inputs.key }} {{ inputs.field }}"
```
```yaml theme={null}
# Get a range
statement: "LRANGE {{ inputs.key }} 0 {{ inputs.count }}"
```
```yaml theme={null}
# Top entries by score
statement: "ZREVRANGE {{ inputs.key }} 0 {{ inputs.count }} WITHSCORES"
```
### Separate adapters for different concerns
Use separate Redis adapters for distinct purposes:
Each adapter can connect to a different Redis database number or a different Redis instance entirely. Tools reference the appropriate adapter by name.
## Troubleshooting
### Connection refused
Verify Redis is running:
```bash theme={null}
redis-cli ping
```
A `PONG` response confirms the server is accessible. Check firewall rules for remote instances.
### Authentication failed
Ensure the password is correct in the connection string:
```
redis://:correct_password@localhost:6379/0
```
Verify that Redis has authentication enabled in its configuration (`requirepass` directive).
### TLS required
Cloud-hosted Redis instances typically require TLS. Switch from `redis://` to `rediss://`:
```
rediss://:password@host:6379/0
```
### Memory issues
Monitor Redis memory usage with `INFO memory`. Configure `maxmemory` and an eviction policy (`maxmemory-policy`) on the Redis server to prevent out-of-memory conditions.
# SQLite
Source: https://docs.hyperterse.com/databases/sqlite
Connect Hyperterse to SQLite for SQL-backed MCP tools — local files, in-memory, or remote libSQL/Turso.
SQLite is an embedded database that runs as a single file or in memory. libSQL and Turso extend SQLite to run remotely with the same SQL, hosted.
Hyperterse supports both: use a local file or `:memory:` for development and embedded workloads, or a libSQL/Turso URL for production with replication and edge sync. Connection pooling and parameterized query execution work the same as other SQL connectors.
For remote databases, Hyperterse connects to your existing database. It does
not create or manage databases — you need to provision your own instance.
* [Turso](https://turso.tech) offers a free tier
* [libSQL](https://libsql.org) can be self-hosted
## Adapter configuration
Create an adapter file in `app/adapters/`:
```yaml app/adapters/sqlite-db.terse theme={null}
connector: sqlite
connection_string: '{{ env.SQLITE_URL }}'
options:
authToken: '{{ env.TURSO_AUTH_TOKEN }}'
```
The connection string format depends on local vs remote:
Use `:memory:`, `file:./path.db`, or absolute path for local file-based
database.
Use `libsql://` or `https://` for hosted Turso; `http://` for local libSQL
instances. WebSocket URLs (`ws://`, `wss://`) are not supported.
Always use [Environment Variables](/reference/environment-variables) for
connection strings and auth tokens. Never commit credentials to source
control.
### Connection options
Pass options through the `options` map. Values are appended directly to the connection string query parameters — no key remapping.
Auth token for remote libSQL/Turso. Required for Turso and secured libSQL
instances.
Replication sync interval for libSQL embedded replicas (e.g. `5s`).
Enable or disable TLS. Use `1` for TLS, `0` to disable (only valid with
explicit port, e.g. local libSQL).
### Recommended permissions
For local file-backed SQLite, ensure the process has read and write access to the database file and its directory. For remote libSQL/Turso, create a database-specific token with the minimum required permissions in the Turso dashboard or libSQL configuration.
### Verify the connection
Start the server and confirm the adapter connects:
```bash theme={null}
hyperterse start
```
A successful connection produces:
```
INFO Connected to adapter: sqlite-db
```
If the connection fails, the server exits immediately with a diagnostic message.
## Usage
SQLite tools execute standard SQL through the adapter. Use `{{ inputs.field }}` placeholders for parameterized values — they are never interpolated as raw SQL.
```yaml app/tools/get-user/config.terse theme={null}
description: 'Retrieve a user by their identifier'
use: sqlite-db
statement: |
SELECT id, name, email, created_at
FROM users
WHERE id = {{ inputs.user_id }}
inputs:
user_id:
type: int
description: 'Primary key of the user record'
auth:
plugin: allow_all
```
SQLite features like JSON functions, full-text search, and window functions are all supported. Use standard SQLite syntax in statements.
## Troubleshooting
### Unsupported URL scheme
If startup fails with an unsupported scheme error, ensure your connection string uses one of:
* `:memory:`, `file:./path.db`, or absolute path for local
* `libsql://`, `https://`, or `http://` for remote
WebSocket URLs (`ws://`, `wss://`) are not supported.
### Remote auth failures
Verify that `authToken` (or `auth_token` / `jwt`) is set correctly and resolves from env:
```yaml theme={null}
options:
authToken: '{{ env.TURSO_AUTH_TOKEN }}'
```
Ensure the token has access to the database and has not expired.
### File open failures
For file-backed SQLite, check that the path exists and the process has read and write permissions. Verify the directory exists when using `file:./relative/path.db`.
# AWS
Source: https://docs.hyperterse.com/deployment/aws
Deploy Hyperterse on Amazon Web Services using ECS Fargate.
ECS Fargate is the recommended way to deploy Hyperterse on AWS. It runs containers without managing servers, provides auto-scaling, and integrates natively with Secrets Manager, CloudWatch, and Application Load Balancers.
## Prerequisites
You need the [AWS CLI](https://aws.amazon.com/cli/) configured with credentials that have permissions for ECR, ECS, and Secrets Manager. You also need Docker installed for building images.
## Deploy to ECS Fargate
```bash theme={null}
hyperterse build -o dist
```
Use the [Docker deployment patterns](/deployment/docker) to create your Dockerfile. Then build and tag the image:
```bash theme={null}
docker build -t hyperterse .
```
Create a repository and push the image:
```bash theme={null}
aws ecr create-repository --repository-name hyperterse --region us-east-1
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin .dkr.ecr.us-east-1.amazonaws.com
docker tag hyperterse:latest .dkr.ecr.us-east-1.amazonaws.com/hyperterse:latest
docker push .dkr.ecr.us-east-1.amazonaws.com/hyperterse:latest
```
```bash theme={null}
aws secretsmanager create-secret \
--name prod/hyperterse/db \
--secret-string "postgresql://user:pass@rds-host:5432/app" \
--region us-east-1
```
```json theme={null}
{
"family": "hyperterse",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam:::role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "hyperterse",
"image": ".dkr.ecr.us-east-1.amazonaws.com/hyperterse:latest",
"portMappings": [{ "containerPort": 8080, "protocol": "tcp" }],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:us-east-1::secret:prod/hyperterse/db"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/hyperterse",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
```
Register it:
```bash theme={null}
aws ecs register-task-definition --cli-input-json file://task-definition.json
```
```bash theme={null}
aws logs create-log-group --log-group-name /ecs/hyperterse --region us-east-1
aws ecs create-service \
--cluster default \
--service-name hyperterse \
--task-definition hyperterse \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}"
```
## RDS integration
Connect to Amazon RDS for managed PostgreSQL or MySQL. Create the instance in the same VPC as your ECS tasks, and ensure the ECS security group allows inbound traffic to the database port.
Update your Secrets Manager secret with the RDS endpoint:
```bash theme={null}
aws secretsmanager update-secret \
--secret-id prod/hyperterse/db \
--secret-string "postgresql://admin:password@hyperterse-db.xxxxx.us-east-1.rds.amazonaws.com:5432/app"
```
## Load balancer
For HTTPS and better traffic distribution, create an Application Load Balancer with a target group pointing to your ECS service on port 8080. Use the `/heartbeat` endpoint for health checks.
## EKS alternative
For Kubernetes-based deployments on AWS, create an EKS cluster and follow the [Kubernetes deployment guide](/deployment/kubernetes). Push your image to ECR and reference it in your Kubernetes manifests.
# Azure
Source: https://docs.hyperterse.com/deployment/azure
Deploy Hyperterse on Microsoft Azure using Container Apps.
Azure Container Apps is the recommended way to deploy Hyperterse on Azure. It provides a fully managed serverless container platform with automatic scaling, built-in HTTPS, and integration with Azure services.
## Prerequisites
You need the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) configured with an active subscription. Install the Container Apps extension:
```bash theme={null}
az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
```
## Deploy to Container Apps
```bash theme={null}
hyperterse build -o dist
```
```bash theme={null}
az group create --name hyperterse-rg --location eastus
az acr create --name hyperterseregistry --resource-group hyperterse-rg --sku Basic
az acr login --name hyperterseregistry
```
Use the [Docker deployment patterns](/deployment/docker) to create your Dockerfile. Then push to Azure Container Registry:
```bash theme={null}
docker build -t hyperterseregistry.azurecr.io/hyperterse:latest .
docker push hyperterseregistry.azurecr.io/hyperterse:latest
```
```bash theme={null}
az containerapp env create \
--name hyperterse-env \
--resource-group hyperterse-rg \
--location eastus
```
```bash theme={null}
az containerapp create \
--name hyperterse \
--resource-group hyperterse-rg \
--environment hyperterse-env \
--image hyperterseregistry.azurecr.io/hyperterse:latest \
--registry-server hyperterseregistry.azurecr.io \
--target-port 8080 \
--ingress external \
--min-replicas 1 \
--max-replicas 10 \
--secrets "db-url=postgresql://user:pass@host:5432/db" \
--env-vars "DATABASE_URL=secretref:db-url"
```
## Azure Database for PostgreSQL
Connect to a managed PostgreSQL instance:
```bash theme={null}
az postgres flexible-server create \
--name hyperterse-db \
--resource-group hyperterse-rg \
--location eastus \
--admin-user admin \
--admin-password YourSecurePassword \
--sku-name Standard_B1ms \
--tier Burstable
```
Ensure the Container Apps environment and the database are on the same virtual network, or configure firewall rules to allow connectivity.
## Key Vault integration
For production credentials, use Azure Key Vault instead of inline secrets:
```bash theme={null}
az keyvault create --name hyperterse-kv --resource-group hyperterse-rg --location eastus
az keyvault secret set --vault-name hyperterse-kv --name database-url --value "postgresql://..."
```
Reference Key Vault secrets in your Container App configuration through managed identity.
## AKS alternative
For Kubernetes-based deployments, create an AKS cluster and follow the [Kubernetes deployment guide](/deployment/kubernetes):
```bash theme={null}
az aks create --name hyperterse-aks --resource-group hyperterse-rg --node-count 3
az aks get-credentials --name hyperterse-aks --resource-group hyperterse-rg
```
# Bare metal
Source: https://docs.hyperterse.com/deployment/bare-metal
Deploy Hyperterse directly on servers without containers or orchestrators.
For environments where containers are not an option, you can deploy the Hyperterse build artifact directly on a server. The artifact is a self-contained directory — copy it to the target machine, set your environment variables, and start the process.
## Deploy the artifact
Build on your CI machine (or locally), then transfer the output:
```bash theme={null}
hyperterse build -o dist
scp -r dist/ deploy@server:/opt/hyperterse/
```
On the server, start the process:
```bash theme={null}
cd /opt/hyperterse
DATABASE_URL="postgresql://..." ./hyperterse serve
```
## Process management with systemd
Use a systemd unit to ensure Hyperterse starts on boot and restarts on failure:
```ini theme={null}
[Unit]
Description=Hyperterse MCP Server
After=network.target
[Service]
Type=simple
User=hyperterse
WorkingDirectory=/opt/hyperterse
ExecStart=/opt/hyperterse/hyperterse serve
Restart=on-failure
RestartSec=5
EnvironmentFile=/opt/hyperterse/.env
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash theme={null}
sudo systemctl enable hyperterse
sudo systemctl start hyperterse
```
Store credentials in the `EnvironmentFile` (`.env`) with restrictive
permissions (`chmod 600`). Do not embed them in the unit file.
## Reverse proxy
Place Hyperterse behind a reverse proxy (nginx, Caddy, HAProxy) for TLS termination, rate limiting, and access control:
```nginx theme={null}
server {
listen 443 ssl;
server_name mcp.example.com;
ssl_certificate /etc/ssl/certs/mcp.pem;
ssl_certificate_key /etc/ssl/private/mcp.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
## Multiple instances
Run multiple Hyperterse processes on different ports behind a load balancer for horizontal scaling:
```bash theme={null}
PORT=8081 ./hyperterse serve &
PORT=8082 ./hyperterse serve &
PORT=8083 ./hyperterse serve &
```
Each instance is independent with its own in-memory cache. Configure your load balancer to distribute traffic across them.
## Checklist
Before going live, verify:
* The process runs as a non-root user.
* Credentials are supplied through environment variables, not hardcoded.
* TLS is terminated at the proxy layer.
* The `/heartbeat` endpoint is monitored.
* Log output is routed to your logging infrastructure.
* Automatic restarts are configured (systemd, supervisor, or equivalent).
# Cloudflare
Source: https://docs.hyperterse.com/deployment/cloudflare
Put Hyperterse behind Cloudflare for edge security, caching, and DDoS protection.
Cloudflare acts as a network layer in front of your Hyperterse deployment. It provides DDoS protection, edge caching, Web Application Firewall (WAF), and automatic SSL — regardless of where your Hyperterse server runs.
Hyperterse is a long-running server process, not a serverless function. Deploy
it on a container platform ([AWS](/deployment/aws), [GCP](/deployment/gcp),
[Azure](/deployment/azure), [Railway](/deployment/railway),
[DigitalOcean](/deployment/digital-ocean)) or [bare
metal](/deployment/bare-metal), then put Cloudflare in front of it.
## Cloudflare Tunnel
Cloudflare Tunnel creates a secure, outbound-only connection from your server to Cloudflare's edge network. Your origin server never needs a public IP address.
```bash theme={null}
# macOS
brew install cloudflare/cloudflare/cloudflared
# Linux
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
chmod +x cloudflared-linux-amd64
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
```
```bash theme={null}
cloudflared tunnel login
cloudflared tunnel create hyperterse
```
Create a configuration file:
```yaml theme={null}
# ~/.cloudflared/config.yml
tunnel:
credentials-file: /path/to/.json
ingress:
- hostname: api.example.com
service: http://localhost:8080
- service: http_status:404
```
```bash theme={null}
cloudflared tunnel run hyperterse
```
For production, run it as a systemd service:
```ini theme={null}
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/cloudflared tunnel run hyperterse
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
In the Cloudflare dashboard, create a CNAME record pointing `api.example.com` to `.cfargotunnel.com` with proxy enabled.
## DNS proxy
If your Hyperterse server already has a public IP, use Cloudflare as a reverse proxy by adding an A or CNAME record with proxy enabled (orange cloud). Set SSL/TLS mode to Full (strict).
## Caching
Configure Cloudflare to cache read-heavy endpoints like `/heartbeat` while bypassing cache for the `/mcp` endpoint, which processes tool calls:
* Cache Rule: `*api.example.com/heartbeat*` — Cache Everything, TTL 1 hour
* Bypass Rule: `*api.example.com/mcp*` — Cache Level: Bypass
## Security hardening
Cloudflare provides several layers of protection:
* WAF: Enable managed rulesets under Security > WAF
* Rate limiting: Create rules to limit requests to `/mcp` (e.g., 100 requests per minute per IP)
* Bot management: Enable Bot Fight Mode to block automated abuse
* DDoS protection: Enabled automatically on all plans
# DigitalOcean
Source: https://docs.hyperterse.com/deployment/digital-ocean
Deploy Hyperterse on DigitalOcean using App Platform or Droplets.
DigitalOcean offers two deployment paths for Hyperterse: App Platform for managed container hosting, and Droplets for full server control. App Platform is recommended for most teams.
## App Platform
App Platform is DigitalOcean's managed platform-as-a-service. It builds, deploys, and manages your container with automatic scaling, SSL, and monitoring.
```bash theme={null}
hyperterse build -o dist
```
Push your Docker image to DigitalOcean Container Registry or Docker Hub:
```bash theme={null}
doctl registry create hyperterse
doctl registry login
docker build -t registry.digitalocean.com/hyperterse/server:latest .
docker push registry.digitalocean.com/hyperterse/server:latest
```
Create an app spec file:
```yaml theme={null}
name: hyperterse
services:
- name: server
image:
registry_type: DOCR
repository: hyperterse/server
tag: latest
http_port: 8080
instance_count: 2
instance_size_slug: basic-xxs
health_check:
http_path: /heartbeat
envs:
- key: DATABASE_URL
value: "${db.DATABASE_URL}"
type: SECRET
databases:
- name: db
engine: PG
production: true
```
Deploy it:
```bash theme={null}
doctl apps create --spec app-spec.yaml
```
App Platform can provision a managed PostgreSQL database as part of the app
spec. The connection string is automatically injected as an environment
variable.
## Droplet deployment
For full server control, deploy to a Droplet and manage the process directly. This follows the same pattern as the [bare metal deployment guide](/deployment/bare-metal).
```bash theme={null}
doctl compute droplet create hyperterse \
--image ubuntu-22-04-x64 \
--size s-1vcpu-1gb \
--region nyc3 \
--ssh-keys
```
```bash theme={null}
hyperterse build -o dist
scp -r dist/ root@:/opt/hyperterse/
```
SSH into the Droplet and set up a systemd service as described in the [bare metal guide](/deployment/bare-metal). Set environment variables in the service file:
```ini theme={null}
[Service]
Environment=DATABASE_URL=postgresql://user:pass@host:5432/db
ExecStart=/opt/hyperterse/hyperterse serve
```
## Managed databases
DigitalOcean Managed Databases provide PostgreSQL, MySQL, MongoDB, and Redis clusters. Create one from the dashboard or CLI:
```bash theme={null}
doctl databases create hyperterse-db --engine pg --region nyc3 --size db-s-1vcpu-1gb
```
Retrieve the connection string and use it in your `.terse` configuration or as an environment variable.
## Load balancer
For multi-instance App Platform deployments, traffic is load-balanced automatically. For Droplet deployments, create a DigitalOcean Load Balancer:
```bash theme={null}
doctl compute load-balancer create \
--name hyperterse-lb \
--region nyc3 \
--forwarding-rules "entry_protocol:https,entry_port:443,target_protocol:http,target_port:8080" \
--health-check "protocol:http,port:8080,path:/heartbeat"
```
# Docker
Source: https://docs.hyperterse.com/deployment/docker
Container deployment patterns for Hyperterse — multi-stage builds, pre-built artifacts, and minimal images.
Containerizing Hyperterse gives you reproducible, portable deployments. The build artifact is a static binary with no external dependencies, so your final image can be extremely small — even `scratch` works.
## Multi-stage build
Build from source inside a container and produce a minimal final image. Source files stay in the build stage and never reach the deployed image.
```dockerfile theme={null}
FROM hyperterse-builder AS builder
WORKDIR /src
COPY . .
RUN hyperterse build -o dist
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
RUN adduser -D -u 1001 hyperterse
COPY --from=builder /src/dist/ /app/
WORKDIR /app
USER hyperterse
EXPOSE 8080
ENTRYPOINT ["./hyperterse", "serve"]
```
This approach is ideal when your CI pipeline builds the container image directly from the repository.
## Pre-built artifact
If your pipeline already produces the `dist/` directory (for example, as a CI artifact), skip the build stage and copy the output directly:
```dockerfile theme={null}
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
RUN adduser -D -u 1001 hyperterse
COPY dist/ /app/
WORKDIR /app
USER hyperterse
EXPOSE 8080
ENTRYPOINT ["./hyperterse", "serve"]
```
## Scratch image
The Hyperterse binary is statically compiled. You can deploy it on a `scratch` base image with no OS layer at all:
```dockerfile theme={null}
FROM scratch
COPY dist/hyperterse /hyperterse
COPY dist/model.bin /model.bin
COPY dist/build/ /build/
EXPOSE 8080
ENTRYPOINT ["/hyperterse", "serve"]
```
This produces the smallest possible image — just the binary and the manifest.
Scratch images have no shell, no package manager, and no debugging tools. Use
them for production deployments where you have external observability in
place. For troubleshooting, use the Alpine-based image.
## Docker Compose
Run Hyperterse alongside your database for local development or simple deployments:
```yaml theme={null}
services:
hyperterse:
build: .
ports:
- '8080:8080'
env_file: .env
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
healthcheck:
test: ['CMD-SHELL', 'pg_isready']
interval: 5s
timeout: 3s
```
## Best practices
* Run as a non-root user. All examples above use a dedicated `hyperterse` user with UID 1001.
* Use `env_file` or secrets for credentials. Never bake connection strings into the image.
* Use health checks. Hyperterse exposes `/heartbeat` — configure Docker's `HEALTHCHECK` or your orchestrator's probes to use it.
* Pin your base image. Use specific tags like `alpine:3.19`, not `alpine:latest`.
# Google Cloud
Source: https://docs.hyperterse.com/deployment/gcp
Deploy Hyperterse on Google Cloud Platform using Cloud Run.
Cloud Run is the recommended way to deploy Hyperterse on GCP. It provides serverless containers with automatic scaling, built-in HTTPS, and pay-per-use pricing. Hyperterse runs as a standard container — no special adaptation needed.
## Prerequisites
You need the [gcloud CLI](https://cloud.google.com/sdk/docs/install) configured with a project that has the Cloud Run, Cloud Build, and Secret Manager APIs enabled:
```bash theme={null}
gcloud config set project PROJECT_ID
gcloud services enable run.googleapis.com cloudbuild.googleapis.com secretmanager.googleapis.com
```
## Deploy to Cloud Run
```bash theme={null}
hyperterse build -o dist
```
Use the [Docker deployment patterns](/deployment/docker) to create your Dockerfile. Then build and push to Google Container Registry:
```bash theme={null}
docker build -t gcr.io/PROJECT_ID/hyperterse:latest .
docker push gcr.io/PROJECT_ID/hyperterse:latest
```
Alternatively, use Cloud Build:
```bash theme={null}
gcloud builds submit --tag gcr.io/PROJECT_ID/hyperterse:latest
```
```bash theme={null}
echo -n "postgresql://user:pass@host:5432/db" | \
gcloud secrets create hyperterse-db --data-file=-
```
Grant your Cloud Run service account access:
```bash theme={null}
PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)")
gcloud secrets add-iam-policy-binding hyperterse-db \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
```
```bash theme={null}
gcloud run deploy hyperterse \
--image gcr.io/PROJECT_ID/hyperterse:latest \
--platform managed \
--region us-central1 \
--port 8080 \
--set-secrets DATABASE_URL=hyperterse-db:latest \
--allow-unauthenticated \
--memory 512Mi \
--cpu 1
```
```bash theme={null}
gcloud run services describe hyperterse \
--platform managed \
--region us-central1 \
--format="value(status.url)"
```
## Cloud SQL integration
Connect to Cloud SQL for managed PostgreSQL or MySQL:
```bash theme={null}
gcloud sql instances create hyperterse-db \
--database-version=POSTGRES_14 \
--tier=db-f1-micro \
--region=us-central1
gcloud run services update hyperterse \
--add-cloudsql-instances PROJECT_ID:us-central1:hyperterse-db \
--region us-central1
```
Use a Unix socket connection string for Cloud SQL:
```
postgresql://user:password@/dbname?host=/cloudsql/PROJECT_ID:us-central1:hyperterse-db
```
## Scaling
Cloud Run scales automatically based on incoming requests. Configure minimum and maximum instances:
```bash theme={null}
gcloud run services update hyperterse \
--min-instances=1 \
--max-instances=10 \
--region us-central1
```
Setting `min-instances=1` avoids cold starts at the cost of continuous billing.
## GKE alternative
For Kubernetes-based deployments on GCP, create a GKE cluster and follow the [Kubernetes deployment guide](/deployment/kubernetes):
```bash theme={null}
gcloud container clusters create hyperterse-cluster \
--num-nodes=3 \
--region=us-central1
```
# Kubernetes
Source: https://docs.hyperterse.com/deployment/kubernetes
Deploy Hyperterse on Kubernetes with health probes, resource limits, and secrets management.
Hyperterse is a stateless, single-binary server — a natural fit for Kubernetes deployments. Each pod runs one instance of `hyperterse serve` from the pre-built artifact image.
## Deployment manifest
```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
name: hyperterse
spec:
replicas: 3
selector:
matchLabels:
app: hyperterse
template:
metadata:
labels:
app: hyperterse
spec:
containers:
- name: hyperterse
image: registry.example.com/hyperterse:latest
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
- name: API_KEY
valueFrom:
secretKeyRef:
name: api-credentials
key: key
livenessProbe:
httpGet:
path: /heartbeat
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /heartbeat
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
resources:
requests:
memory: '64Mi'
cpu: '100m'
limits:
memory: '256Mi'
cpu: '500m'
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
```
## Service
Expose the deployment internally or externally:
```yaml theme={null}
apiVersion: v1
kind: Service
metadata:
name: hyperterse
spec:
selector:
app: hyperterse
ports:
- port: 80
targetPort: 8080
type: ClusterIP
```
For external access, use an Ingress controller or change the type to `LoadBalancer`.
## Health probes
Hyperterse exposes `/heartbeat` on the configured port. Use it for both liveness and readiness probes:
* Liveness — Restart the pod if the process is unresponsive. Set a reasonable `initialDelaySeconds` to allow connector initialization.
* Readiness — Remove the pod from the service until all connectors are ready. The heartbeat endpoint only responds after the server is fully initialized.
## Secrets
Store credentials in Kubernetes Secrets and inject them as environment variables. Never include connection strings or API keys in your container image or ConfigMaps.
```yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
url: 'postgresql://user:password@db-host:5432/app'
```
## Scaling
Hyperterse is stateless. Scale horizontally by increasing `replicas`. Each instance maintains its own in-memory cache — no shared state between pods.
If your tools use MCP session management, enable sticky sessions through your ingress controller or service mesh to send subsequent requests from the same session to the same pod.
## Security hardening
The manifest above follows security best practices:
* Read-only root filesystem prevents runtime modifications to the binary or manifest.
* Non-root user (UID 1001) limits the blast radius of a container escape.
* Resource limits prevent a single pod from consuming all node resources.
See [Production hardening](/security/production-hardening) for additional recommendations.
# Overview
Source: https://docs.hyperterse.com/deployment/overview
Build artifacts, serve from pre-compiled output, and deploy to any environment.
Hyperterse separates compilation from execution. You build once in a trusted environment, then deploy the resulting artifact anywhere. The artifact contains everything the server needs to run — no source files, no build tools, no package manager.
This separation gives you deterministic deployments (the validated manifest is the one that runs), faster startup (no filesystem scanning at boot), and a smaller attack surface (source files stay out of the artifact).
## Build
Compile your project into a self-contained output directory:
```bash theme={null}
hyperterse build -o dist
```
This loads `.hyperterse` from the current directory, discovers all adapters and tools, bundles scripts, and writes the deployment artifact.
To build from a different project directory, pass a positional argument:
```bash theme={null}
hyperterse build path/to/project -o dist
```
The output directory contains:
| Artifact | Description |
| ------------------------- | ------------------------------------------------------- |
| `hyperterse` | Runtime binary |
| `model.bin` | Serialized manifest (adapters, tools, config) |
| `build/vendor.js` | Shared dependency bundle (if tools import npm packages) |
| `build/tools//*.js` | Per-tool script bundles |
Always run `hyperterse validate` before building. The build command also
validates, but catching errors early keeps your CI pipeline fast.
## Serve
Boot from a pre-built artifact:
```bash theme={null}
hyperterse serve dist/
```
The serve command deserializes `model.bin`, reconstructs the project, initializes connectors in parallel, registers MCP tools, and starts the HTTP server. There is no re-parsing of source files.
You can also point directly to the manifest:
```bash theme={null}
hyperterse serve dist/model.bin
```
## Deployment workflow
The principle is: build once, deploy the artifact. Do not rebuild in the target environment.
Run `hyperterse validate` to catch configuration errors before building.
Run `hyperterse build -o dist` to produce the deployment artifact.
Run `hyperterse serve dist/` to verify the artifact works before shipping.
Package the `dist/` directory into your deployment target — a container
image, a tarball, or a direct copy.
## Environment configuration
The `dist/` directory contains no secrets. All credentials are supplied at runtime through environment variables:
```bash theme={null}
DATABASE_URL="postgresql://..." API_KEY="sk-..." hyperterse serve dist/
```
Set variables through your platform's secrets management: Docker Compose `env_file`, Kubernetes Secrets, cloud provider environment config, or platform dashboards.
See [Environment variables reference](/reference/environment-variables) for the full list of supported variables.
## Horizontal scaling
Hyperterse is a single-process stateless server. Scale by running multiple instances behind a load balancer:
* Each instance has its own in-memory cache — no inter-instance coordination needed.
* All instances require the same database endpoints and environment variables.
* Use sticky sessions if you need MCP session continuity across requests.
## CI/CD integration
Automate the validate-build-deploy cycle in your pipeline:
```yaml theme={null}
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: hyperterse validate
- run: hyperterse build -o dist
- uses: actions/upload-artifact@v4
with:
name: hyperterse-dist
path: dist/
```
## Next steps
Choose a deployment method based on your infrastructure:
Container images for portable deployment
Orchestrated deployment at scale
Direct binary deployment on servers
Or deploy to a specific cloud provider:
ECS Fargate
Cloud Run
Container Apps
Managed containers
App Platform
Edge security layer
Frontend proxy
# Railway
Source: https://docs.hyperterse.com/deployment/railway
Deploy Hyperterse on Railway with zero infrastructure management.
Railway is the fastest path from build artifact to running server. It detects your Dockerfile, provisions infrastructure, and gives you a public URL — all in a single deploy command.
## Deploy to Railway
```bash theme={null}
hyperterse build -o dist
```
Use the [Docker deployment patterns](/deployment/docker) to create your Dockerfile, or use a minimal example:
```dockerfile theme={null}
FROM node:20-slim
WORKDIR /app
COPY dist/ ./dist/
COPY node_modules/ ./node_modules/
EXPOSE 8080
CMD ["npx", "hyperterse", "serve"]
```
Adjust the Dockerfile to match your project structure. The key requirement is that the `dist/` directory containing the build artifact is available at runtime.
```bash theme={null}
npm install -g @railway/cli
railway login
```
```bash theme={null}
railway init
railway up
```
Railway detects the Dockerfile, builds the image, and deploys it.
```bash theme={null}
railway variables set DATABASE_URL="postgresql://user:pass@host:5432/db"
```
Or configure variables in the Railway dashboard under your service's settings.
In the Railway dashboard, go to your service's Settings and generate a public domain. Your Hyperterse MCP endpoint will be available at `https:///mcp`.
## Railway-managed databases
Railway provides one-click managed databases. Add a PostgreSQL or MySQL plugin from the dashboard, and Railway automatically injects the connection string as an environment variable.
```bash theme={null}
railway add --plugin postgresql
```
Reference the injected variable in your `.terse` configuration:
```yaml theme={null}
adapters:
main:
connector: postgresql
connection_string: '{{ env.DATABASE_URL }}'
```
## Custom domains
In the Railway dashboard, navigate to Settings → Domains and add your custom domain. Railway provisions TLS certificates automatically.
## Scaling
Railway supports horizontal scaling through the dashboard. Increase the number of replicas under your service's Settings → Scaling section. Each replica runs an independent instance of `hyperterse serve`.
# Vercel
Source: https://docs.hyperterse.com/deployment/vercel
Connect a Vercel-hosted frontend to a Hyperterse MCP server.
Vercel excels at hosting frontends and serverless functions. For Hyperterse — a long-running server process — the recommended architecture is to deploy Hyperterse on a container platform and connect your Vercel frontend to it through API handlers.
Hyperterse requires a persistent server process. Deploy it on
[Railway](/deployment/railway), [AWS](/deployment/aws),
[GCP](/deployment/gcp), [Azure](/deployment/azure),
[DigitalOcean](/deployment/digital-ocean), or [bare
metal](/deployment/bare-metal), then configure your Vercel project to
communicate with it.
## Architecture
Your Vercel frontend communicates with Hyperterse through server-side API handlers. This keeps your Hyperterse endpoint private and lets Vercel handle CORS, authentication, and caching at the edge.
```
Browser → Vercel Edge → API Handler → Hyperterse Server → Database
```
## Configure the proxy
In the Vercel dashboard, go to Settings → Environment Variables and add:
```
HYPERTERSE_URL=https://your-hyperterse-deployment.example.com
```
Forward MCP requests from your frontend to the Hyperterse server:
```typescript theme={null}
// pages/api/mcp.ts (Next.js Pages Router)
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const response = await fetch(`${process.env.HYPERTERSE_URL}/mcp`, {
method: req.method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(req.body),
});
const data = await response.json();
res.status(response.status).json(data);
}
```
Push to your connected Git repository, or deploy manually:
```bash theme={null}
vercel --prod
```
## Rewrites
As an alternative to an API handler, use Vercel rewrites to proxy requests directly:
```json theme={null}
{
"rewrites": [
{
"source": "/mcp",
"destination": "https://your-hyperterse-deployment.example.com/mcp"
}
]
}
```
Add this to your `vercel.json`. Vercel forwards matching requests to your Hyperterse server without exposing its URL to the client.
## Considerations
* Latency: Place your Hyperterse server in the same region as your primary user base to minimize round-trip time.
* Authentication: Add authentication in your API handler or Vercel middleware before forwarding requests to Hyperterse.
* Timeouts: Vercel serverless functions have execution time limits. For long-running tool calls, ensure your Hyperterse queries complete within the Vercel timeout window, or use streaming responses.
# Installation
Source: https://docs.hyperterse.com/installation
Install the Hyperterse CLI—the agentic server framework for agents (A2A), MCP tools, prompts, and resources.
Hyperterse is an agentic server framework. The CLI validates your project and runs MCP plus optional A2A agent routes from one binary.
## System requirements
| Requirement | Minimum |
| ---------------- | ------------------------------------------------------------------ |
| Operating system | Linux (amd64, arm64), macOS (amd64, arm64), Windows (amd64) |
| Bun or Node.js | Bun 1.0+ or Node.js 18+ (only if tool scripts import npm packages) |
The Hyperterse binary is a single statically linked executable. It has no runtime dependencies — no external runtime is needed to build or serve manifests.
## Install
Hyperterse is available on multiple package managers. The recommended way to install is via the installer script, but you can also install manually.
```bash cURL theme={null}
curl -fsSL https://hyperterse.com/install | bash
```
```bash npm theme={null}
npm install -g hyperterse
```
```bash bun theme={null}
bun install -g Hyperterse
```
```bash brew theme={null}
brew install hyperterse/tap/hyperterse
```
Once installed, you can verify the installation by running:
```bash theme={null}
hyperterse --version
```
## Upgrade
To upgrade to the latest version, run:
```bash theme={null}
hyperterse upgrade
```
Include pre-release builds with `--prerelease`, or target a specific major version with `--major 3` or `--major next`.
# Introduction
Source: https://docs.hyperterse.com/introduction
The agentic server framework—agents, tools, prompts, and resources with auth, caching, and observability.
Any serious AI workload needs the same backbone: safe access to data, clear inputs, authentication, caching, observability, and protocols that models and agent runtimes actually speak.
Hyperterse is an agentic server framework. You describe agents, tools, prompts, and resources in declarative config. The engine validates and compiles them, then serves MCP for tool, prompt, and resource surfaces, and agent HTTP when you define agents. You keep ownership of data and business rules; the framework handles plumbing and protocol edges.
## What you can build
You can expose databases and custom logic as tools, ship reusable prompt templates and resources, and run A2A-compatible agents on shared adapters and policies. A typical database-backed tool looks like this:
```yaml theme={null}
description: 'Get user by ID'
use: primary-db
statement: 'SELECT id, name, email FROM users WHERE id = {{ inputs.user_id }}'
inputs:
user_id:
type: int
required: true
auth:
plugin: api_key
policy:
value: '{{ env.API_KEY }}'
```
That yields an MCP tool named `get-user` that runs your query, validates inputs, and enforces API keys—without hand-rolling a service layer for each action.
## How it works
Add adapters, tools, and optionally prompts, resources, and agents. Each
surface is declared in config; Hyperterse validates and links them.
Run `hyperterse build` to validate configuration, bundle scripts, and emit a
deployable artifact.
Run `hyperterse serve` (or `hyperterse start` in development) so MCP and
agent routes go live from that artifact.
During development, `hyperterse start --watch` rebuilds when files change so
you can skip manual compile cycles.
## Key capabilities
Declarative agents with model providers, tool access rules, and dedicated
HTTP endpoints for agent-style workflows.
One declarative definition per tool—database-backed or script-backed—with
discovery and validation at compile time.
Reusable prompt templates with arguments, completions, and multi-message
scaffolding for MCP clients.
Static or templated read-only context for `resources/list` and
`resources/read`, with optional subscriptions when content changes.
Connect to PostgreSQL, MySQL, SQLite, MongoDB, or Redis. Pooling, health
checks, and shutdown are handled for you.
TypeScript handlers and transforms when pure config is not enough. Scripts
run in a sandbox with `fetch` and `console`.
Attach auth per tool with built-in plugins or your own—no global middleware
required.
Cache tool results globally or per tool with a TTL so repeat calls stay
cheap.
OpenTelemetry tracing, metrics, and structured logging for operations and
debugging.
## Next steps
Set up Hyperterse on your machine in under a minute.
Install, scaffold, run, and optional MCP checks against the sample tool.
# Quickstart
Source: https://docs.hyperterse.com/quickstart
The agentic server framework—docs entry points, install, run, optional MCP checks.
Hyperterse is an agentic server framework: one project can ship agents, tools, prompts, and resources together—plus databases, auth, caching, and observability—in one process.
MCP is how most clients list and call tools, read resources, and fetch prompts. When you add agents, they get their own HTTP routes (A2A-style), separate from MCP. You do not need both on day one; add surfaces as your product grows.
## Where to start
Long-running agents with models, permissions over tools, and standard agent HTTP—without wiring a second server.
Actions exposed to MCP: queries, APIs, or scripts, with validation and auth in one declarative layer.
Read-only context clients can pull in—static text, files, or parameterized templates.
Reusable prompt templates with arguments and client-side completion hints.
[Project structure](/concepts/project-structure) — layout and discovery.
## Baseline: install and run
### Install the CLI
```bash theme={null}
curl -fsSL https://hyperterse.com/install | bash
```
[Installation](/installation) — package managers and verification.
### Scaffold a project
```bash theme={null}
hyperterse init
```
You get a root config, a sample tool, and starter agent-skills helpers. The exact tree depends on the template.
### Review the root configuration
Open `.hyperterse`:
```yaml theme={null}
name: myconfig
server:
port: 8080
log_level: 3
```
`name` is the service id. `server.port` is the HTTP port. `server.log_level` is verbosity: 1 errors only, 2 warnings, 3 info, 4 debug.
### Start the server
```bash theme={null}
hyperterse start
```
The CLI loads your config, validates the project, packages TypeScript tools when needed, and serves MCP. For reload on save while developing:
```bash theme={null}
hyperterse start --watch
```
### Verify the server is running
```bash theme={null}
curl http://localhost:8080/heartbeat
```
```json theme={null}
{ "success": true }
```
This only proves HTTP is up—not that a database or model provider is healthy.
## Optional: verify MCP tools
The template includes a hello-world tool.
### List registered tools
```bash theme={null}
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}' | jq
```
You should see each tool’s name, description, and input schema.
### Inspect the sample tool
Open the hello-world tool’s `config.terse` next to its handler.
```yaml theme={null}
description: 'Hello world tool'
handler: './handler.ts'
inputs:
name:
type: string
description: 'Name to greet.'
auth:
plugin: allow_all
```
### Call the tool
```bash theme={null}
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "hello-world",
"arguments": { "name": "Hyperterse" }
},
"id": 3
}' | jq
```
### Validate before deploying
```bash theme={null}
hyperterse validate
```
This catches bad config, missing adapters, schema mistakes, and script packaging errors before you ship.
## Next steps
* [Project structure](/concepts/project-structure) — How discovery and layout work.
* [MCP transport](/runtime/mcp-transport) — How Streamable HTTP and JSON-RPC fit together.
* [Agents overview](/agents/overview) — When to use agent routes vs MCP alone.
* [CLI reference](/reference/cli) — Commands and flags.
# Adapter
Source: https://docs.hyperterse.com/reference/adapter-config
Complete field reference for adapter definition files.
Each file in `app/adapters/` defines one adapter — a named binding between Hyperterse and your database. Adapter files are YAML documents with a fixed schema.
## Full schema
```yaml app/adapters/primary-db.terse theme={null}
name: primary-db
connector: postgres
connection_string: 'postgresql://user:pass@host:5432/db'
options:
sslmode: require
max_connections: '10'
```
## Field reference
Adapter identifier, referenced by tools via `use`. Must be unique across all adapters. Must match `^[a-zA-Z][a-zA-Z0-9_-]*$`.
Default: filename without the `.terse` extension (e.g., `primary-db` from `primary-db.terse`).
Connector type. Determines which database driver to use.
Allowed values: `postgres`, `mysql`, `mongodb`, `redis`, `sqlite`
Database connection URI. Supports `{{ env.VAR }}` placeholders for secrets.
```yaml theme={null}
connection_string: 'postgresql://{{ env.DB_USER }}:{{ env.DB_PASS }}@{{ env.DB_HOST }}:5432/{{ env.DB_NAME }}'
```
Driver-specific key-value options. All values must be strings.
```yaml theme={null}
options:
sslmode: 'require'
connect_timeout: '10'
```
## Connection string formats
```
postgresql://user:password@host:port/database?param=value
```
Standard PostgreSQL DSN. Supports all driver parameters in the query string or `options` map.
Common options:
| Key | Example | Description |
| ------------------ | ----------------------------------- | ----------------------- |
| `sslmode` | `require`, `disable`, `verify-full` | SSL mode |
| `connect_timeout` | `"10"` | Timeout in seconds |
| `application_name` | `"hyperterse"` | Name reported to server |
```
user:password@tcp(host:port)/database?param=value
```
Standard MySQL DSN. Options are typically passed as query parameters in the connection string.
```
mongodb://user:password@host:port/database
mongodb+srv://user:password@cluster.example.com/database
```
Standard MongoDB URI. Supports replica sets and SRV records. Options are embedded in the connection string.
```
redis://user:password@host:port/db_number
```
Standard Redis URI. Database number as path component. TLS, password, and other options are embedded in the URI.
```
# local
:memory:
file:./data/app.db
/absolute/path/to/app.db
# remote (libSQL / Turso)
libsql://db-name-org.turso.io
https://db-name-org.turso.io
```
SQLite supports local files (or `:memory:`) and remote libSQL/Turso databases.
Use `libsql://`, `https://`, or `http://` for remote; WebSocket URLs are not supported.
`options` are appended as query parameters. Example:
```yaml theme={null}
connector: sqlite
connection_string: 'libsql://db-name-org.turso.io'
options:
authToken: '{{ env.TURSO_AUTH_TOKEN }}'
sync_interval: '5s'
```
## Lifecycle
Hyperterse loads adapter definitions from `app/adapters/*.terse`.
Connector type, connection string presence, and name uniqueness are verified
at compile time.
At startup, all referenced adapters initialize their connections in
parallel.
Tools resolve adapters by name when executing statements.
All connectors close concurrently on termination.
If any adapter fails to connect during initialization, the server does not
start.
## JSON Schema
Editor validation: `schema/adapter.terse.schema.json`. Associate with `**/adapters/*.terse`. See [Configuration schemas](/reference/configuration-schemas).
# Agent
Source: https://docs.hyperterse.com/reference/agent-config
Complete field reference for declarative agent definition files.
Each `app/agents/*/config.terse` defines one declarative agent exposed at `/agent/{name}`.
## Full schema
```yaml app/agents/support/config.terse theme={null}
name: support
description: 'Support assistant'
instruction: 'Resolve user support requests and call tools when needed.'
model:
provider: openai_compatible
model: gpt-4o-mini
options:
base_url: 'https://api.openai.com/v1'
api_key: '{{ env.OPENAI_API_KEY }}'
tool_access:
mode: inherit
```
## Field reference
Agent name. Must be unique across all discovered agents and match
`^[a-z][a-z0-9_-]*$`.
Optional agent summary used in app listings and diagnostics.
Primary system instruction passed to the model runtime.
Model provider configuration for this agent.
Model provider identifier.
Supported values:
* `gemini`, `google_ai_studio`
* `vertex`, `vertex_ai`
* `openai_compatible`, `openai`
Provider names are normalized when the agent loads (lowercased, `-` converted to `_`).
Provider-specific model name.
Provider-specific options map.
In config, option values may be scalar (`string`, `number`, `boolean`). At
build time, values are stringified for use by the agent.
Model option values support `{{ env.VAR_NAME }}` substitution when the agent
model starts. Missing variables fail startup.
For secrets, use provider default env vars (for example `OPENAI_API_KEY`,
`GOOGLE_API_KEY`) or `api_key: "{{ env.YOUR_SECRET_VAR }}"`.
For provider-specific keys and env fallbacks, see
[Model providers](/agents/model-providers).
Tool access policy for this agent. Optional — defaults to `inherit` when omitted,
which uses the root-level defaults from `.hyperterse` (`agents.tool_access`).
If no root-level default is set either, the effective mode is `allow_all`.
One of:
* `inherit` — use root-level defaults from `.hyperterse` (`agents.tool_access`).
* `allow_all` — agent can call every discovered tool.
* `allow_none` — agent cannot call any project tools.
* `allow_list` — agent can call only the listed tools.
Required when `mode=allow_list`. Every tool name must exist in the project.
## JSON Schema
Editor validation: `schema/agent.terse.schema.json`. Associate with
`**/agents/**/config.terse`. See [Configuration schemas](/reference/configuration-schemas).
# Command line reference
Source: https://docs.hyperterse.com/reference/cli
Complete command and flag reference for the Hyperterse CLI.
All commands load `.env` files from the working directory when present.
```bash theme={null}
hyperterse [command] [flags]
```
Print the installed version and exit.
## `hyperterse start`
Read config, validate the project, package tool scripts, open database connections, and start the HTTP server (MCP and optional A2A routes).
```bash theme={null}
hyperterse start [path] [flags]
```
Without arguments, loads `.hyperterse` from the current directory. A positional `path` can point to a directory containing `.hyperterse` or directly to a config file.
### Flags
Server port. Overrides config and `PORT` env var.
Short: `-p`
Resolution order: `--port` → `server.port` → `PORT` env → `8080`
Log verbosity. `1` = error, `2` = warn, `3` = info, `4` = debug. Overrides config.
Resolution order: `--verbose` (4) → `--log-level` → `server.log_level` → `3`
Sets log level to `4` (debug). Overrides `--log-level`.
Configuration as an inline YAML string instead of reading from a file.
Short: `-s`
Hot-reload on `.terse` and `.ts` file changes. Recompiles and restarts
automatically.
Comma-separated tag filter. Prefix a tag with `-` to exclude it.
Stream logs to `/tmp/.hyperterse/logs/` in addition to stdout.
### Examples
```bash theme={null}
hyperterse start
hyperterse start --watch
hyperterse start path/to/project
hyperterse start -p 9090 --verbose
```
## `hyperterse serve`
Boot from a pre-built manifest without re-parsing source files.
```bash theme={null}
hyperterse serve [manifest-or-dir] [flags]
```
Without arguments, searches for `model.bin` in the current directory. A positional argument can point to a directory containing `model.bin` or directly to a manifest file.
### Flags
Server port. Overrides the value embedded in the manifest and `PORT` env var.
Short: `-p`
Log verbosity. Overrides the value embedded in the manifest.
Sets log level to `4` (debug).
Comma-separated tag filter.
Stream logs to file.
### Examples
```bash theme={null}
hyperterse serve dist/
hyperterse serve
```
## `hyperterse build`
Compile the project into a deployable output directory.
```bash theme={null}
hyperterse build [path] [flags]
```
Without arguments, loads `.hyperterse` from the current directory.
### Flags
Output directory for build artifacts.
Short: `-o`
Remove the output directory before building.
### Output
### Examples
```bash theme={null}
hyperterse build
hyperterse build -o release --clean-dir
hyperterse build path/to/project -o dist
```
## `hyperterse validate`
Check configuration and project structure without starting the server.
```bash theme={null}
hyperterse validate [path] [flags]
```
### Flags
Configuration as an inline YAML string instead of reading from a file.
Short: `-s`
### Checks
* Root config schema compliance
* Adapter completeness (`connector` and `connection_string` required)
* Tool validity (exactly one of `use` or `handler`)
* Adapter name uniqueness
* Input type correctness
* Script file resolution
* Bundle compilation
### Exit codes
| Code | Meaning |
| ---- | -------------------------------------------- |
| `0` | Validation passed |
| `1` | Validation failed — errors printed to stderr |
### Examples
```bash theme={null}
hyperterse validate
hyperterse validate path/to/project
```
## `hyperterse init`
Scaffold a new project with starter files.
```bash theme={null}
hyperterse init
```
### Created files
| Path | Content |
| ------------------------------------------- | ------------------------------------------------------ |
| `.hyperterse` | Root config with defaults |
| `app/tools/hello-world/config.terse` | Hello world script-backed tool |
| `app/tools/hello-world/handler.ts` | TypeScript handler |
| `.agents/skills/hyperterse-docs/SKILL.md` | Agent Skills skill pointing to docs |
| `.agents/skills/hyperterse-agents/SKILL.md` | Agent Skills skill for creating agents with Hyperterse |
Does not overwrite existing files.
## `hyperterse upgrade`
Upgrade the installed binary.
```bash theme={null}
hyperterse upgrade [flags]
```
### Flags
Include pre-release versions in the upgrade check.
Target a specific major version number, or `next` for the latest major.
### Examples
```bash theme={null}
hyperterse upgrade
hyperterse upgrade --prerelease
hyperterse upgrade --major next
hyperterse upgrade --major 3
```
## `hyperterse completion`
Generate shell completion scripts. Hidden from default help.
```bash theme={null}
hyperterse completion [bash|zsh|fish|powershell]
```
# Configuration schemas
Source: https://docs.hyperterse.com/reference/configuration-schemas
JSON Schema files for editor-time validation of .hyperterse and .terse configuration files.
Hyperterse ships seven role-based JSON Schema files for editor-time validation, autocompletion, and inline documentation.
| Schema file | Target files | Purpose |
| ----------------------------------- | ------------------------------- | --------------------------------------- |
| `schema/root.terse.schema.json` | `.hyperterse` | Root service configuration |
| `schema/adapter.terse.schema.json` | `app/adapters/*.terse` | Adapter definitions |
| `schema/tool.terse.schema.json` | `app/tools/*/config.terse` | Tool definitions |
| `schema/agent.terse.schema.json` | `app/agents/*/config.terse` | Agent definitions |
| `schema/prompt.terse.schema.json` | `app/prompts/**/*.terse` | Prompt definitions |
| `schema/resource.terse.schema.json` | `app/resources/**/config.terse` | Resource and resource-template settings |
## Generation
Schemas are generated from the project’s source schema definitions:
```bash theme={null}
make generate
```
The generator reads connector and primitive type enums and emits JSON Schema files with the correct `enum` constraints.
## Editor setup
Point your editor at the schema files to enable autocompletion, hover docs, and inline validation for `.terse` files.
### VS Code / Cursor
Add to `.vscode/settings.json`:
```json theme={null}
{
"yaml.schemas": {
"schema/root.terse.schema.json": "**/.hyperterse",
"schema/adapter.terse.schema.json": "**/adapters/*.terse",
"schema/tool.terse.schema.json": "**/tools/**/config.terse",
"schema/agent.terse.schema.json": "**/agents/**/config.terse",
"schema/prompt.terse.schema.json": "**/prompts/**/*.terse",
"schema/resource.terse.schema.json": "**/resources/**/config.terse"
}
}
```
Requires the [YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml).
### JetBrains IDEs
In the IDE: Settings → Languages & Frameworks → Schemas and DTDs → JSON Schema Mappings. Add one mapping per schema with the appropriate file pattern.
## Schema highlights
Each schema enforces required fields, value constraints, and structural rules for its configuration level.
### Root (`root.terse.schema.json`)
* `name` — Required. Pattern: `^[a-z][a-z0-9_-]*$`.
* `version` — Optional string.
* `root` — String (default discovery root: `app`).
* `tools.directory` — String (default: `tools`).
* `tools.cache` — `enabled` (boolean), `ttl` (integer).
* `tools.search.limit` — Integer (default: `10`) controlling max MCP `search` results.
* `adapters.directory` — String (default: `adapters`).
* `prompts` — Either:
* `{ directory: string }` discovery config (default: `prompts`), or
* inline prompt definitions array.
* `resources` — Either:
* `{ directory: string }` discovery config (default: `resources`), or
* inline concrete resource definitions array.
* `resource_templates` — Optional inline resource-template definitions array.
* `agents.directory` — String (default: `agents`).
* `agents.tool_access.mode` — Enum: `allow_all`, `allow_none`, `allow_list`.
* `agents.tool_access.tools` — Array of tool names; required when `mode=allow_list`.
* `server.port` — Integer or string.
* `server.log_level` — Integer, 1–4.
* `build.out` / `build.out_dir` — String.
* `build.clean_dir` — Boolean.
### Adapter (`adapter.terse.schema.json`)
* `connector` — Required. Enum: `postgres`, `redis`, `mysql`, `mongodb`, `sqlite`.
* `connection_string` — Required string.
* `name` — Optional.
* `options` — Optional map.
### Tool (`tool.terse.schema.json`)
* `description` — Required string.
* `use` — String.
* `statement` — String.
* `inputs` — Map of typed definitions.
* `handler` — Optional script handler path (`path#exportName` supported; defaults to `export default`).
* `mappers` — Optional `input` / `output` mapper paths (`path#exportName` supported; defaults to `export default`).
* `auth` — `plugin`, `policy`.
* `cache` — `enabled`, `ttl`.
* Constraint: exactly one of `use` or `handler` is required (enforced via `oneOf`).
### Prompt (`prompt.terse.schema.json`)
* `name` — Optional prompt name override (defaults to filename).
* `title` — Optional string.
* `description` — Optional string.
* `arguments` — Optional map keyed by argument name with:
* `title` (string)
* `description` (string)
* `required` (boolean)
* `completion` (string array)
* `messages` — Required non-empty array of `{ role, text }` where role is one of `user`, `assistant`, `system`.
See [Prompt configuration](/reference/prompt-config).
### Resource (`resource.terse.schema.json`)
* Supports both concrete resources and URI templates in one schema.
* Shared optional fields: `name`, `title`, `description`, `mime_type`.
* Concrete resource mode:
* `uri` (required)
* one of `text` or `file` (required)
* Resource template mode:
* `uri_template` (required)
* one of `text_template` or `file_template` (required)
* optional `arguments` map keyed by argument name with:
* `title` (string)
* `description` (string)
* `required` (boolean)
* `completion` (string array)
See [Resource configuration](/reference/resource-config).
### Agent (`agent.terse.schema.json`)
* `name` — Required string. Pattern: `^[a-z][a-z0-9_-]*$`.
* `description` — Optional string.
* `instruction` — Required string.
* `model.provider` — Required string provider identifier.
* `model.model` — Required provider-specific model name.
* `model.options` — Optional scalar map (`string`/`number`/`boolean`) for provider options.
* `tool_access` — Optional object. Defaults to `inherit` when omitted.
* `tool_access.mode` — Enum: `inherit`, `allow_all`, `allow_none`, `allow_list`.
* `tool_access.tools` — Optional array of tool names, required when `mode=allow_list`.
## Role ownership
* Root scope (`.hyperterse`) owns service-level defaults and discovery wiring (`root`, `tools`, `adapters`, `prompts`, `resources`, `agents`, and optional inline `resource_templates`).
* Adapter scope (`app/adapters/*.terse`) owns connector and connection configuration.
* Tool scope (`app/tools/**/config.terse`) owns execution/query behavior and auth/cache controls.
* Prompt scope (`app/prompts/**/*.terse`) owns MCP prompt messages and arguments.
* Resource scope (`app/resources/**/config.terse`) owns MCP resources and URI templates.
* Agent scope (`app/agents/**/config.terse`) owns agent behavior: `instruction`, model, and tool access policy.
## Keeping schemas in sync
When configuration shapes change, schemas and documentation must update in the same change:
1. Regenerate schema artifacts in `schema/`.
2. Update field definitions in reference documentation.
3. Update `.vscode/settings.json` globs if file-role matching changed.
4. Verify checklists/reference docs include all current schema files:
* `root.terse.schema.json`
* `adapter.terse.schema.json`
* `tool.terse.schema.json`
* `agent.terse.schema.json`
* `prompt.terse.schema.json`
* `resource.terse.schema.json`
# Environment variables
Source: https://docs.hyperterse.com/reference/environment-variables
Environment variables for the Hyperterse server, CLI, and `{{ env.* }}` substitution in config.
## Configuration substitution
You can use `{{ env.VAR_NAME }}` placeholders in any string value across your configuration files. Hyperterse resolves these when the server starts.
### Supported locations
| Config location | Example |
| ---------------------------- | --------------------------------------------------------------------------------------------- |
| Adapter `connection_string` | `"postgresql://{{ env.DB_USER }}:{{ env.DB_PASS }}@{{ env.DB_HOST }}:5432/{{ env.DB_NAME }}"` |
| Adapter `options` values | `sslmode: "{{ env.DB_SSL_MODE }}"` |
| Tool `statement` | `"SELECT * FROM {{ env.TABLE_PREFIX }}_users WHERE id = {{ inputs.user_id }}"` |
| Tool `auth.policy` values | `value: "{{ env.API_KEY }}"` |
| Agent `model.options` values | `api_key: "{{ env.OPENAI_API_KEY }}"` |
| Root config `server.port` | `port: "{{ env.PORT }}"` |
### Resolution behavior
* Placeholders resolve at the point of use: connector initialization, statement execution, auth check, or agent model initialization.
* Missing variables cause a hard failure with an error identifying the unresolved variable.
* Placeholders are not recursive — a variable whose value contains `{{ env.OTHER }}` is not expanded further.
* The `.env` file in the working directory is loaded before command execution.
## Runtime variables
Fallback port when not set via CLI flag or config.
Precedence: `--port` flag → `server.port` config → `PORT` env → `8080`
Fallback API key for the `api_key` auth plugin when `auth.policy.value` is not set on a tool.
Precedence: `auth.policy.value` config → `HYPERTERSE_API_KEY` env
Comma-separated log tag filter. Limits which tags appear in log output.
Precedence: `--log-tags` flag → `HYPERTERSE_LOG_TAGS` env
## Agent model provider variables
These variables are used when resolving model configuration for declarative agents
(`app/agents/*/config.terse`).
| Variable | Used by | Purpose | Precedence |
| ----------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `OPENAI_API_KEY` | `openai_compatible`, `openai` | Fallback API key for OpenAI-compatible model calls. | `model.options.api_key` → `OPENAI_API_KEY` |
| `GOOGLE_API_KEY` | `gemini`, `google_ai_studio`, `vertex`, `vertex_ai` (optional on Vertex) | Fallback API key for Gemini/Vertex model calls. | `model.options.api_key` → `GOOGLE_API_KEY` |
| `GOOGLE_CLOUD_PROJECT` | `vertex`, `vertex_ai` | Fallback Vertex project id. | `model.options.project` → `GOOGLE_CLOUD_PROJECT` |
| `GOOGLE_CLOUD_LOCATION` | `vertex`, `vertex_ai` | Primary Vertex location fallback. | `model.options.location` → `GOOGLE_CLOUD_LOCATION` → `GOOGLE_CLOUD_REGION` |
| `GOOGLE_CLOUD_REGION` | `vertex`, `vertex_ai` | Secondary Vertex location fallback when location is still empty. | Used only after `model.options.location` and `GOOGLE_CLOUD_LOCATION` are absent/empty |
### Agent model options and env substitution
* `model.options` string values support `{{ env.VAR_NAME }}` substitution when the agent model starts.
* Missing variables referenced in `model.options` placeholders fail startup with a clear error.
* This is in addition to provider-level fallback variables in the table above.
## `.env` file
The CLI loads `.env` from the current working directory when present. Standard `KEY=VALUE` format. Blank lines and `#`-prefixed lines are ignored.
```bash .env theme={null}
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
API_KEY=sk-secret-key-value
PORT=9090
```
Shell-set environment variables take precedence over `.env` values. The file
provides defaults, not overrides.
## Security guidance
Do not commit `.env` to version control. Add it to `.gitignore`.
* Use `{{ env.VAR }}` for all credentials. Connection strings, API keys, and tokens must reference environment variables — never hardcode secrets in `.terse` files.
* Rotation requires restart. Environment variables resolve at startup. For zero-downtime rotation, use a secrets manager that updates the environment and signals a restart.
* Set production log level. Debug logs may include substituted statement text. Use log level `2` (warn) or `3` (info) in production.
# Prompt
Source: https://docs.hyperterse.com/reference/prompt-config
Complete field reference for prompt definition files.
Each `app/prompts/**/*.terse` file defines one MCP prompt, including message templates and optional argument metadata for completion support.
## Full schema
```yaml app/prompts/summarize-release.terse theme={null}
name: summarize-release
title: Release summary helper
description: Summarize release notes for a target audience.
arguments:
audience:
title: Audience
description: Target audience for summary tone and detail
required: true
completion: ["engineering", "product", "customers"]
tone:
description: Desired writing tone
completion: ["concise", "detailed"]
messages:
- role: system
text: You summarize releases for {{ audience }}.
- role: user
text: Write a {{ tone }} release summary.
```
## Field reference
Prompt identifier exposed through MCP `prompts/list`.
Default: filename without `.terse`.
Prompt names must be unique and should follow lower-kebab/lower-snake style
(`^[a-z][a-z0-9_-]*$`).
Optional human-friendly prompt title.
Optional prompt description returned by `prompts/list` and `prompts/get`.
Optional map of argument definitions keyed by argument name.
Optional display title for the argument.
Optional argument description.
Whether callers are expected to provide this argument.
Optional static completion values. Used by `completion/complete` for
prompt references.
Ordered message templates for prompt rendering.
One of `user`, `assistant`, or `system`.
Message template text. Supports interpolation placeholders like
`{{ audience }}`.
## Validity rules
* `messages` must contain at least one item.
* Prompt names must be unique across the project.
* Argument names must be unique within each prompt.
* Message roles must be one of `user`, `assistant`, `system`.
## Runtime mapping
* Prompt metadata and arguments are surfaced in MCP `prompts/list`.
* Message templates are rendered through MCP `prompts/get`.
* Argument `completion` values are used by MCP `completion/complete` (`ref/prompt`).
## JSON Schema
Editor validation: `schema/prompt.terse.schema.json`. Associate with
`**/prompts/**/*.terse`. See [Configuration schemas](/reference/configuration-schemas).
# Resource
Source: https://docs.hyperterse.com/reference/resource-config
Complete field reference for resource and resource-template definition files.
Each `app/resources/**/config.terse` file defines one MCP resource surface. A resource folder can represent either:
* a concrete resource (`uri`), or
* a resource template (`uri_template`).
## Full schema
```yaml app/resources/release-notes/config.terse theme={null}
uri: memory://release-notes/latest
name: release-notes
title: Latest release notes
description: Markdown release notes for the current release.
mime_type: text/markdown
file: ./content/release-notes.md
```
```yaml app/resources/order-by-id/config.terse theme={null}
uri_template: memory://orders/{id}
name: order-by-id
title: Order by ID
description: Order payload template resolved by id.
mime_type: application/json
text_template: '{"id":"{{ id }}","status":"pending"}'
arguments:
id:
description: Order identifier
required: true
completion: ["1001", "1002", "1003"]
```
## Field reference
Concrete resource URI. Use for fixed resources read via `resources/read`.
Exactly one of `uri` or `uri_template` must be defined.
URI template (RFC 6570 style) for parameterized resources.
Exactly one of `uri` or `uri_template` must be defined.
Optional display name.
Default: resource folder name (directory containing `config.terse`).
Optional human-friendly title.
Optional description surfaced in MCP resource metadata.
Optional MIME type override for returned content.
Inline concrete content for `uri` resources.
For concrete resources, define one of `text` or `file`.
File path for concrete `uri` resources.
Paths are resolved relative to the `config.terse` directory when not absolute.
For concrete resources, define one of `text` or `file`.
Inline template content for `uri_template` resources.
Supports interpolation placeholders like `{{ id }}`.
For template resources, define one of `text_template` or `file_template`.
File path template for `uri_template` resources.
Supports interpolation placeholders like `./docs/{{ id }}.md`.
Paths are resolved relative to the `config.terse` directory when not absolute.
For template resources, define one of `text_template` or `file_template`.
Optional map of URI template argument metadata keyed by argument name.
Optional display title.
Optional argument description.
Whether callers are expected to provide this argument.
Optional static completion values used by `completion/complete` for
resource template references.
## Validity rules
* One of `uri` or `uri_template` is required.
* `uri` + `uri_template` together is invalid.
* For `uri` resources: one of `text` or `file` is required.
* For `uri_template` resources: one of `text_template` or `file_template` is required.
* Resource URIs must be unique across concrete resources.
* URI templates must be unique across template resources.
## Runtime mapping
* Concrete resources appear in MCP `resources/list`.
* Template resources appear in MCP `resources/templates/list`.
* Both are resolved via MCP `resources/read`.
* Template `arguments.completion` values are used by MCP `completion/complete` (`ref/resource`).
* Resource updates on model reload emit MCP notifications (`resources/list_changed`, `resources/updated`).
## JSON Schema
Editor validation: `schema/resource.terse.schema.json`. Associate with
`**/resources/**/config.terse`. See [Configuration schemas](/reference/configuration-schemas).
# Root
Source: https://docs.hyperterse.com/reference/root-config
Complete field reference for the .hyperterse root configuration file.
The root configuration file (`.hyperterse`) defines service-level settings and discovery settings. Adapter and tool definitions are discovered from the filesystem, and prompts/resources can be configured through discovery directories or optional inline lists.
## Full schema
```yaml .hyperterse theme={null}
name: my-service
version: 1.0.0
root: app
tools:
directory: tools
search:
limit: 10
cache:
enabled: true
ttl: 60
adapters:
directory: adapters
prompts:
directory: prompts
resources:
directory: resources
resource_templates: []
agents:
directory: agents
tool_access:
mode: allow_none
server:
port: 8080
log_level: 3
build:
out: dist
clean_dir: false
```
## Field reference
Service identifier. Used in logging, tracing, and manifest metadata. Must
match `^[a-z][a-z0-9_-]*$`.
Service version. Informational. Included in manifest metadata and startup
logs.
Base directory for adapter/tool discovery. Relative to the root config path.
Tool discovery and global tool cache defaults.
Tools directory relative to `root`.
Global cache defaults for DB-backed tools.
Enable in-memory tool result caching globally.
Default TTL in seconds for cached tool results.
Global search defaults for MCP `search` results.
Maximum number of ranked tools returned from a single MCP `search` call.
Adapter discovery settings.
Adapters directory relative to `root`.
Prompt configuration:
Prompts directory relative to `root`.
Inline prompt definitions as an array of prompt objects (`name`, optional
`arguments`, and required `messages`).
Resource configuration:
Resources directory relative to `root`.
Inline concrete resources as an array of objects (`uri`, metadata, and one
of `text` or `file`).
Optional inline URI-template resources. Each item defines `uri_template`,
metadata, template content (`text_template` or `file_template`), and optional
`arguments`.
Agent discovery settings and project-level default tool access policy.
Agents directory relative to `root`.
Default tool access policy for agents. Individual agents can override this
in `config.terse`.
One of `allow_all`, `allow_none`, `allow_list`.
Required when `mode=allow_list`. Contains the allowed project tool
names.
For file-based prompt/resource configs, see [Prompt configuration](/reference/prompt-config) and [Resource configuration](/reference/resource-config).
Server runtime settings.
TCP port for the HTTP/MCP server.
Resolution order: `--port` flag → `server.port` config → `PORT` env → `8080`
Log verbosity. `1` = error, `2` = warn, `3` = info, `4` = debug.
Resolution order: `--verbose` (4) → `--log-level` flag → `server.log_level` config → `3`
Build output settings.
Output directory for build artifacts. `out_dir` is an equivalent alias.
Resolution order: `--out` flag → `build.out` config → `dist`
Remove output directory before building.
## Environment variable substitution
String values support `{{ env.VAR_NAME }}` placeholders, resolved at runtime:
```yaml theme={null}
server:
port: '{{ env.PORT }}'
```
Missing variables cause a startup failure. Verify all referenced variables are
set before deploying.
## JSON Schema
Editor validation: `schema/root.terse.schema.json`. See [Configuration schemas](/reference/configuration-schemas).
## Examples
```yaml .hyperterse theme={null}
name: my-service
```
All other fields use defaults: port `8080`, log level `3`, caching disabled.
```yaml .hyperterse theme={null}
name: production-service
version: 2.1.0
server:
port: "{{ env.PORT }}"
log_level: 2
tools:
cache:
enabled: true
ttl: 300
build:
out: release
clean_dir: true
```
# Tool
Source: https://docs.hyperterse.com/reference/tool-config
Complete field reference for tool definition files.
Each `app/tools/*/config.terse` defines one MCP tool — its execution strategy, inputs, mappers, authentication, and caching behavior.
## Full schema
```yaml app/tools/get-user-profile/config.terse theme={null}
name: get-user-profile
description: 'Retrieve a user profile by primary key'
use: primary-db
statement: |
SELECT id, name, email, created_at
FROM users
WHERE id = {{ inputs.user_id }}
inputs:
user_id:
type: int
description: 'User primary key'
mappers:
input: './validate-input.ts'
output: './format-output.ts'
auth:
plugin: api_key
policy:
value: '{{ env.API_KEY }}'
cache:
enabled: true
ttl: 120
```
## Field reference
MCP tool name. Must be unique across all tools.
Default: directory name (e.g., `get-user` from `app/tools/get-user/`).
Human-readable tool description. Exposed in the `tools/list` MCP response.
Strongly recommended for agent discoverability.
Adapter name for statement execution. References an adapter identifier defined in `app/adapters/`.
`use` must be a single adapter name. Array values are rejected during
validation.
Exactly one of `use` or `handler` must be set. A tool cannot define both.
SQL query or database command. Supports `{{ env.VAR }}` and `{{ inputs.field }}` placeholders. Use YAML multiline syntax (`|`) for readability.
```yaml theme={null}
statement: |
SELECT id, name, email
FROM users
WHERE id = {{ inputs.user_id }}
```
Map of input parameter definitions. Each key becomes a named parameter in the MCP tool's input schema.
Input data type. Allowed values: `string`, `int`, `float`, `boolean`, `datetime`.
Human-readable description. Exposed in the MCP input schema for agent consumption.
Whether the input can be omitted by the caller.
Default value when the caller omits this input. Always a string; converted to the declared type at runtime.
Optional handler script path for fully script-backed tools. When set, it replaces
database execution entirely. The handler receives inputs and returns results directly.
Use `path#exportName` to call a non-default export.
Without `#exportName`, Hyperterse calls the script's `export default` (function name ignored).
Optional mapper script paths for pre/post processing. All paths are relative to
the tool directory.
Input mapper script path. Runs before statement execution. Receives raw inputs
and returns modified inputs.
Use `path#exportName` to call a non-default export.
Without `#exportName`, Hyperterse calls the script's `export default` (function name ignored).
Output mapper script path. Runs after statement execution. Receives raw results
and returns modified results.
Use `path#exportName` to call a non-default export.
Without `#exportName`, Hyperterse calls the script's `export default` (function name ignored).
If mapper/handler scripts are omitted, convention-based discovery applies — see [Scripts](/concepts/scripts).
Tool-level authentication. Omitting this block entirely makes the tool unauthenticated.
Registered auth plugin name. Built-in plugins: `allow_all`, `api_key`.
Plugin-specific parameters. For `api_key`, set `value` to the expected key or an `{{ env.VAR }}` reference.
```yaml theme={null}
auth:
plugin: api_key
policy:
value: "{{ env.API_KEY }}"
```
Per-tool cache settings. Overrides the global cache configuration from `.hyperterse`.
Whether caching is active for this tool. Inherits from `tools.cache.enabled` if not set.
Cache TTL in seconds for this tool. Inherits from `tools.cache.ttl` if not set.
## Validity rules
A tool is valid when exactly one of these conditions is met:
1. `use` is defined (database-backed tool).
2. `handler` is defined (script-backed tool).
Tools with neither `use` nor `handler` — or with both — are rejected during
validation.
## Runtime mapping
* Tool configs become live tool definitions when the server starts or reloads.
* Project tools are discoverable via MCP `tools/call` on `search` (ranked metadata results).
* Project tools execute via MCP `tools/call` on `execute` with:
* `tool` = resolved tool name
* `inputs` = caller payload validated against `inputs` definitions
* `auth` runs before execution and can reject requests.
* `mappers.input` runs before statement/handler execution; `mappers.output` runs after.
* `handler` tools run script-backed execution; `use` + `statement` tools run connector-backed execution.
* `cache` settings map to runtime in-memory result caching policy.
* Tool calls emit MCP progress and logging notifications during execution.
`tools/list` intentionally returns the transport entry tools (`search`,
`execute`) as a core runtime design. Your configured project tools are
discovered through `search` and invoked through `execute`.
## JSON Schema
Editor validation: `schema/tool.terse.schema.json`. Associate with `**/tools/**/config.terse`. See [Configuration schemas](/reference/configuration-schemas).
# A2A transport
Source: https://docs.hyperterse.com/runtime/a2a-transport
Per-agent A2A HTTP surface in Hyperterse—JSON-RPC, agent card, tasks, and streaming—separate from MCP.
Every agent you define is served at its own route prefix. Clients use JSON-RPC over `POST`, in the A2A (Agent2Agent) style: agent card, messages, tasks, and optional streaming. That is separate from MCP—there are no MCP tool-list or prompt endpoints on these routes. For tools, prompts, and resources, use [MCP transport](/runtime/mcp-transport) at `/mcp`.
Configuration and permissions live in the agent guides. Start with [Agents
overview](/agents/overview); for every method and curl example, see [Runtime
API](/agents/runtime-api).
## How it fits next to MCP
| Surface | Path | Role |
| ------- | -------------------- | ------------------------------------------------------------------------- |
| MCP | `/mcp` | Tools (`search` / `execute`), prompts, resources, completion, MCP session |
| A2A | `/agent/{agentName}` | Agent card, messaging, tasks, streaming, push config |
MCP and A2A share one Hyperterse server and the same tool-access rules for agents, but the protocols and client contracts differ.
## Endpoints
| Endpoint | Method(s) | Purpose |
| ------------------------------------------------ | --------- | ------------------------------------------------------ |
| `/agent/{agentName}/.well-known/agent-card.json` | `GET` | Public agent card for discovery |
| `/agent/{agentName}` | `POST` | A2A JSON-RPC (messages, tasks, streaming, push config) |
Replace `{agentName}` with your agent’s name (see [Agents overview](/agents/overview)).
## What the transport covers
* Discovery — clients fetch the agent card before calling the JSON-RPC endpoint.
* Messaging — `SendMessage` and `SendStreamingMessage` (SSE when using streaming).
* Tasks — create, poll, list, cancel, and subscribe to task updates when responses include trackable work.
* Push — list, create, or delete task push notification configuration when you use those flows.
Method names and payloads match the A2A-style contract in [Runtime API](/agents/runtime-api).
## Protocol
Requests use JSON-RPC 2.0 over HTTP `POST` on `/agent/{agentName}`. The surface is A2A-compatible so standard agent clients can integrate without a custom protocol. For the exact methods Hyperterse exposes, see [Runtime API](/agents/runtime-api).
## Session and reload
* Task state is how you continue work across turns (not a legacy session-only API).
* After a model reload, agent routes match your latest config; see [Runtime API](/agents/runtime-api) for behavior notes.
## CORS
Hyperterse applies CORS on agent routes so browser and cross-origin clients can call them in development. Tighten rules in production like any HTTP API (see [Production hardening](/security/production-hardening)).
## Related docs
* [MCP transport](/runtime/mcp-transport) — Streamable HTTP MCP on `/mcp`
* [Runtime API](/agents/runtime-api) — Full A2A method list and examples
* [Agents overview](/agents/overview) — Declarative agents and routing
* [Tool access](/agents/tool-access) — Which tools an agent may call
# Caching
Source: https://docs.hyperterse.com/runtime/caching
In-memory tool result caching, cache key derivation, TTL, and override behavior.
Hyperterse includes a built-in in-memory cache. The cache stores tool results keyed by tool name and substituted statement, reducing redundant connector execution for identical requests.
Caching applies to DB-backed tools only. Script-backed tools (handler-only) bypass the cache.
## Cache key derivation
Each entry is identified by:
```
cache_key = hash(tool_name + substituted_statement)
```
The same tool with different input values produces different cache entries. Identical inputs for the same tool produce a cache hit.
## Configuration
Caching is controlled at two levels: global defaults in the root config and per-tool overrides.
### Global defaults
Set default caching in `.hyperterse`:
```yaml theme={null}
tools:
cache:
enabled: true
ttl: 60
```
| Field | Type | Default | Description |
| --------- | ------- | ------- | -------------------------------------------------- |
| `enabled` | boolean | `false` | Whether caching is active for all DB-backed tools. |
| `ttl` | integer | `120` | Time-to-live in seconds. |
### Per-tool override
```yaml theme={null}
cache:
enabled: true
ttl: 30
```
```yaml theme={null}
cache:
enabled: false
```
### Precedence
1. Tool-level config (highest priority).
2. Global root config (`tools.cache`).
3. Runtime defaults (`enabled: false`, `ttl: 120`).
## Behavior
The cache operates as a read-through layer in front of connectors and handler scripts.
### On execution
1. Before connector execution, the executor checks the cache using the derived key.
2. Hit — cached result returned immediately; connector is not called.
3. Miss — connector executes; result stored with configured TTL before being returned.
### Eviction
* TTL-based — entries expire after their configured TTL.
* Capacity-based — the cache enforces memory bounds (128 MiB default). When approaching capacity, least-recently-used entries are evicted.
### No explicit invalidation
There is no API for manual cache invalidation. Entries are evicted only by TTL or capacity pressure. For immediate invalidation, disable caching on affected tools and manage cache externally (e.g., through a Redis adapter with handler logic).
## Characteristics
| Property | Value |
| ------------- | -------------------------------------------------------------- |
| Scope | Process-local. Not shared between instances. |
| Storage | In-memory. No persistence across restarts. |
| Thread safety | Concurrent-safe. Reads and writes do not block execution. |
| Serialization | Results are cloned on store and retrieval to prevent mutation. |
| Distributed | None. Each instance maintains its own cache. |
## When to enable caching
Enable for:
* Read-heavy tools with stable data (reference tables, configuration lookups).
* Expensive queries where staleness within the TTL window is acceptable.
* High-frequency tools where connector load reduction matters.
Disable for:
* Write operations or tools that must return real-time data.
* Highly variable inputs where cache fill exceeds hit rate.
* Tools where result freshness is critical for correctness.
## Monitoring
Cache hit/miss status is included in OpenTelemetry trace spans when observability is configured. Monitor hit ratio to evaluate whether TTL values are effective for your workload.
# Execution pipeline
Source: https://docs.hyperterse.com/runtime/execution-pipeline
Deterministic execution flow for tool invocations from tool resolution through response serialization.
Every `tools/call` request passes through a deterministic pipeline. The stages are identical for DB-backed and script-backed tools — only the execution step differs. If any stage fails, execution halts immediately and an error response is returned.
## Tool resolution
Hyperterse matches the tool name from `tools/call` to the tool definition it loaded for that server run.
If no tool matches, a JSON-RPC error is returned with code `-32601`.
## Authentication
If the tool defines an `auth` block, the configured plugin is invoked with the request context and policy parameters.
* Plugin returns `nil`: authentication passes; the request continues.
* Plugin returns an error: the request stops; the error goes back to the caller.
* No `auth` block: stage is skipped entirely.
## Input transform
If `mappers.input` is configured or discovered by convention, the mapper script's `export default` function executes in the embedded runtime.
Payload:
```json theme={null}
{
"inputs": { "user_id": 42 },
"tool": "get-user"
}
```
The returned object replaces the inputs for all subsequent stages. Throwing an error stops processing.
If no input transform is configured, inputs pass through unchanged.
## Execution
The execution stage branches based on the tool's configuration. Only one path runs per invocation.
### Script-backed tools
When `handler` is configured, the handler script's `export default` function executes in the embedded runtime. The return value becomes the execution result.
### DB-backed tools
When `use` and `statement` are configured, the executor runs six substeps:
1. Input validation — validate all declared inputs against type definitions. Missing required inputs and type mismatches produce errors.
2. Environment substitution — resolve `{{ env.VAR }}` placeholders. Missing variables produce errors.
3. Input substitution — replace `{{ inputs.field }}` placeholders with post-transform values. Substitution is textual.
4. Cache check — if caching is enabled, compute the cache key from tool name + statement hash. On hit, return cached result and skip connector execution.
5. Connector execution — run the statement against the configured connector and return row/object results.
6. Cache store — on miss, store the result with the configured TTL.
## Output transform
If `mappers.output` is configured or discovered by convention, the mapper script's `export default` function executes in the embedded runtime.
Payload:
```json theme={null}
{
"results": [{ "id": 42, "name": "Jane Doe", "email": "jane@example.com" }],
"tool": "get-user"
}
```
The returned value replaces the result for response serialization. Throwing an error stops processing.
If no output transform is configured, the raw result is used directly.
## Response serialization
The final result is JSON-encoded and wrapped in an MCP content block:
```json theme={null}
{
"content": [
{
"type": "text",
"text": "[{\"id\":42,\"name\":\"Jane Doe\",\"email\":\"jane@example.com\"}]"
}
]
}
```
## Error propagation
| Stage | Error condition | JSON-RPC code |
| ------------------- | ------------------------------ | ------------- |
| Tool resolution | Tool name not found | `-32601` |
| Authentication | Plugin returns error | `-32000` |
| Input transform | Script throws | `-32000` |
| Input validation | Missing input or type mismatch | `-32000` |
| Env substitution | Missing variable | `-32000` |
| Connector execution | Query error | `-32000` |
| Handler execution | Script throws | `-32000` |
| Output transform | Script throws | `-32000` |
Error messages from scripts and connectors are included in the response. Stack traces are logged at debug level but not exposed to callers.
## Observability hooks
Each pipeline execution is instrumented with OpenTelemetry spans when tracing is enabled. Span attributes include tool name, execution stage, cache hit/miss, connector type, and duration. Sensitive values are redacted. See [Observability](/runtime/observability).
# MCP transport
Source: https://docs.hyperterse.com/runtime/mcp-transport
Full MCP 2025-11-25 surface in Hyperterse, including tools, prompts, resources, completion, notifications, and session behavior.
Hyperterse is a full MCP server for tools, prompts, and resources. Discovery and execution use Streamable HTTP and JSON-RPC 2.0 on `/mcp`. There are no separate REST endpoints per tool, no GraphQL layer, and no custom tool protocol beyond MCP.
Hyperterse serves MCP over Streamable HTTP (`/mcp`) using JSON-RPC 2.0. It supports:
* two-tool entrypoints (`search`, `execute`)
* first-class prompts
* first-class resources and URI templates
* argument completion
* resource subscriptions
* progress and logging notifications
* session continuity across model reloads
Declarative agents use A2A on `/agent/{agentName}`—separate from MCP.
See [A2A transport](/runtime/a2a-transport); method details in [Runtime
API](/agents/runtime-api).
## Endpoints
| Endpoint | Methods | Purpose |
| ------------ | ----------------------- | --------------------------------------------------- |
| `/mcp` | `GET`, `POST`, `DELETE` | MCP Streamable HTTP transport and session lifecycle |
| `/heartbeat` | `GET` | Liveness endpoint (`{"success": true}`) |
## Protocol version
Hyperterse implements MCP `2025-11-25`.
## Capability surface
Hyperterse capability exposure includes:
* `tools` (`listChanged`)
* `prompts` (`listChanged`) when prompt definitions exist
* `resources` (`listChanged`, `subscribe`) when resources/templates are configured
* `completions`
* `logging`
## MCP method support
| Method | Direction | Notes |
| ---------------------------------- | ---------------- | ---------------------------------------------------- |
| `initialize` | client -> server | Standard MCP handshake |
| `notifications/initialized` | client -> server | Standard MCP post-init notification |
| `ping` | client -> server | Supported |
| `tools/list` | client -> server | Returns transport entry tools (`search`, `execute`) |
| `tools/call` | client -> server | Calls `search` or `execute` |
| `prompts/list` | client -> server | Lists configured prompts |
| `prompts/get` | client -> server | Resolves prompt messages with argument interpolation |
| `resources/list` | client -> server | Lists concrete resources |
| `resources/templates/list` | client -> server | Lists URI template resources |
| `resources/read` | client -> server | Reads concrete or template-resolved content |
| `resources/subscribe` | client -> server | Validates subscription target and enables updates |
| `resources/unsubscribe` | client -> server | Unsubscribes from resource updates |
| `completion/complete` | client -> server | Provides completions for prompt/template arguments |
| `notifications/progress` | client -> server | Supported (logged) |
| `notifications/roots/list_changed` | client -> server | Supported (logged) |
| `notifications/cancelled` | client -> server | Supported (MCP cancellation) |
## Tool entrypoint design
`tools/list` intentionally exposes exactly two transport entry tools:
* `search` — discover project tools by natural language over tool metadata
* `execute` — execute a project tool by name with validated inputs
This is a core Hyperterse design choice: project tools are discovered and invoked through these two entrypoints.
### Search result limit
```yaml .hyperterse theme={null}
tools:
search:
limit: 10
```
If omitted, default is `10`.
## Prompt behavior
Prompts come from:
* prompt definitions discovered in your project (see [Project structure](/concepts/project-structure)), unless you override the directory in `.hyperterse`
* inline `prompts` in root config
`prompts/get` interpolates `{{ argumentName }}` placeholders from request arguments.
```json theme={null}
{
"jsonrpc": "2.0",
"method": "prompts/get",
"params": {
"name": "summarize-release",
"arguments": { "audience": "engineering", "tone": "concise" }
},
"id": 11
}
```
## Resource behavior
Resources come from:
* resource definitions discovered in your project (see [Project structure](/concepts/project-structure)), unless you override the directory in `.hyperterse`
* inline `resources` and `resource_templates` in root config
`resources/read` resolves:
* concrete `uri` resources (`text` or `file`)
* `uri_template` resources (`text_template` or `file_template`) using URI path values
```json theme={null}
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": { "uri": "memory://orders/1001" },
"id": 21
}
```
## Completion behavior
`completion/complete` is supported for:
* prompt argument completions (`ref/prompt`)
* resource template argument completions (`ref/resource`)
```json theme={null}
{
"jsonrpc": "2.0",
"method": "completion/complete",
"params": {
"ref": { "type": "ref/prompt", "name": "summarize-release" },
"argument": { "name": "audience", "value": "eng" }
},
"id": 31
}
```
## Notifications emitted by Hyperterse
### List-change notifications
When model content changes during reload, Hyperterse emits:
* `notifications/tools/list_changed` (if tool digest changed)
* `notifications/prompts/list_changed`
* `notifications/resources/list_changed` (resources/templates list changes)
### Resource update notifications
When concrete resource content changes for an existing URI during reload:
* `notifications/resources/updated` is emitted for the changed URI.
### Tool call observability notifications
During `tools/call` execution for `search` and `execute`:
* `notifications/progress` is emitted (start/completion)
* `notifications/message` is emitted with structured log payloads
## Cancellation behavior
Cancellation from the client propagates through tool execution when connectors and scripts support it.
## Session behavior and reload continuity
* Streamable HTTP session IDs use `Mcp-Session-Id`.
* `DELETE /mcp` terminates a session.
* After a model reload, MCP stays on the same server process and keeps active sessions.
* Sessions keep working and receive list or update notifications when tools, prompts, or resources change.
## CORS and headers
Hyperterse sends CORS headers on `/mcp` responses, including:
* `Access-Control-Allow-Origin: *`
* `Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS`
* `Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key, Mcp-Session-Id`
* `Access-Control-Expose-Headers: Mcp-Session-Id`
## Example: two-tool flow
```json theme={null}
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
```
```json theme={null}
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "query": "orders by status" }
},
"id": 2
}
```
```json theme={null}
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "execute",
"arguments": {
"tool": "get-orders",
"inputs": { "status": "pending" }
}
},
"id": 3
}
```
## Heartbeat
```bash theme={null}
curl http://localhost:8080/heartbeat
```
```json theme={null}
{ "success": true }
```
Heartbeat shows the HTTP server is up, not that every database adapter is healthy.
## Related docs
* [A2A transport](/runtime/a2a-transport) — Agent HTTP on `/agent/{agentName}`
* [Prompts](/concepts/prompts)
* [Resources](/concepts/resources)
* [Prompt configuration](/reference/prompt-config)
* [Resource configuration](/reference/resource-config)
* [Root configuration](/reference/root-config)
* [Configuration schemas](/reference/configuration-schemas)
# Observability
Source: https://docs.hyperterse.com/runtime/observability
OpenTelemetry tracing and metrics, structured logging, attribute redaction, and collector integration.
Hyperterse integrates with OpenTelemetry for distributed tracing and metrics. Tracing covers the execution path, database connectors, and MCP traffic. Structured logging provides operational visibility at every level.
## Tracing
When tracing is on, Hyperterse records spans for each stage of tool execution:
| Span | Attributes |
| ------------------------ | -------------------------------------------- |
| MCP request handling | Method, tool name, request ID |
| Auth plugin execution | Plugin name, tool name, success/failure |
| Input transform | Tool name, script path |
| Connector execution | Adapter name, connector type, cache hit/miss |
| Output transform | Tool name, script path |
| Connector initialization | Adapter name, connector type |
Traces follow the OpenTelemetry specification and export to any compatible backend: Jaeger, Zipkin, Grafana Tempo, Datadog, AWS X-Ray (via ADOT collector), or any OTLP receiver.
## Metrics
| Metric | Type | Description |
| ------------------------ | --------- | --------------------------------- |
| Tool invocation count | Counter | `tools/call` requests per tool |
| Execution duration | Histogram | End-to-end time per tool |
| Cache hit/miss ratio | Counter | Hits vs. misses per query |
| Connector execution time | Histogram | Time in connector `Execute` calls |
| Auth failure count | Counter | Rejections per plugin |
Metrics export through the configured OpenTelemetry meter provider.
## Structured logging
Hyperterse uses a tagged structured logger with the following fields:
* Timestamp — ISO 8601.
* Level — ERROR (1), WARN (2), INFO (3), DEBUG (4).
* Tag — component identifier: `runtime`, `executor`, `connector`, `mcp`, `auth`.
* Message — descriptive text.
* Fields — structured key-value pairs (tool name, adapter, duration, error details).
### Log levels
| Level | Value | Description |
| ----- | ----- | --------------------------------------------------------------------- |
| Error | 1 | Unrecoverable failures. Connector init failures, fatal config errors. |
| Warn | 2 | Recoverable issues. Cache misses on expected hits, slow queries. |
| Info | 3 | Operational events. Startup, shutdown, tool registration, requests. |
| Debug | 4 | Diagnostic detail. Substituted statements, auth context, cache keys. |
Configure via `server.log_level` in `.hyperterse`, or `--log-level` / `--verbose` CLI flags.
### Log routing
| Flag | Description |
| ------------------- | ----------------------------------------- |
| `--log-file ` | Write logs to a file instead of stderr. |
| `--log-tags ` | Filter output to specific component tags. |
## Attribute redaction
Sensitive values are redacted before export:
* Connection strings — replaced with `[REDACTED]` in trace spans.
* API keys — auth policy values excluded from trace attributes.
* Statement parameters — input values in substituted statements logged at debug level only.
Redaction is applied at the observability contract layer, not per-exporter.
## Configuration
```yaml theme={null}
server:
log_level: 3
observability:
tracing:
enabled: true
endpoint: 'http://localhost:4318/v1/traces'
metrics:
enabled: true
endpoint: 'http://localhost:4318/v1/metrics'
```
When tracing or metrics are not configured, they default to disabled.
## Collector integration
Hyperterse exports via OTLP over HTTP. Point the endpoint to your collector:
| Backend | Configuration |
| ------------- | --------------------------------- |
| Jaeger | OTLP receiver on port 4318 |
| Grafana Tempo | OTLP receiver |
| Datadog Agent | OTLP ingestion endpoint |
| AWS X-Ray | ADOT collector with OTLP receiver |
### Quick start with Jaeger
```bash theme={null}
docker run -d --name jaeger \
-p 16686:16686 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
```
Set the tracing endpoint to `http://localhost:4318/v1/traces` and view traces at `http://localhost:16686`.
# Input safety
Source: https://docs.hyperterse.com/security/input-safety
Statement substitution model, security implications, and defensive patterns.
Hyperterse uses textual substitution for `{{ inputs.field }}` placeholders. Before a statement reaches the connector driver, the executor performs two passes:
1. Environment substitution — `{{ env.VAR }}` placeholders are replaced with process environment values.
2. Input substitution — `{{ inputs.field }}` placeholders are replaced with string representations of validated input values.
The resulting statement is a complete string passed to the connector's `Execute` method. No parameterized query binding occurs at the substitution layer.
## Implications
* Values are inserted directly into the statement text.
* The connector receives the statement as a single string.
* Statement safety depends on query design and input validation.
* This is functionally equivalent to string interpolation — it does not provide prepared-statement injection protection.
## Built-in protections
Hyperterse validates and sanitizes inputs before they reach connectors or scripts.
### Input type validation
| Check | Behavior |
| ------------------- | ------------------------------------------------------------------------------------ |
| Required inputs | Missing required inputs produce an error before execution. |
| Type conversion | Values are converted to the declared type. Invalid conversions produce a type error. |
| Default application | Omitted optional inputs with defaults receive the default value. |
Type validation constrains the value space: an `int` input only accepts numbers, a `boolean` only accepts `true`/`false`. This eliminates injection risk for non-string types.
### Credential isolation
* Connection strings are server-side only.
* Auth policy values are resolved from environment variables and never returned in responses.
* Trace attributes redact sensitive values.
### Auth enforcement
* Tool-level auth runs before any input processing.
* Auth failures stop the request immediately.
## What the framework does not protect against
* SQL injection via string inputs — a `string`-typed input has no content restriction. Malicious SQL can be injected through string placeholders.
* Statement manipulation via crafted values — any input that contributes to statement structure (not just data values) is an injection vector.
* Business logic abuse — negative IDs, excessive limits, and semantically invalid values are not caught by type validation.
## Defensive patterns
Apply the following practices to minimize the surface area exposed to untrusted input.
### Use strict types
Prefer `int`, `float`, and `boolean` over `string`. Numeric and boolean types have constrained value spaces.
```yaml theme={null}
inputs:
user_id:
type: int
active:
type: boolean
```
### Validate strings in transforms
For `string`-typed inputs, validate format in an input transform:
```typescript theme={null}
export default function inputTransform(payload: {
inputs: Record
tool: string
}) {
const { email } = payload.inputs
if (
typeof email !== 'string' ||
!email.match(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)
) {
throw new Error('Invalid email format')
}
return payload.inputs
}
```
### Keep statements narrow
Use inputs only in value positions:
```yaml theme={null}
# Safe: input as a WHERE value
statement: "SELECT * FROM users WHERE id = {{ inputs.user_id }}"
# Dangerous: input as a table or column name
statement: "SELECT * FROM {{ inputs.table_name }}"
```
### Use handlers for dynamic queries
When a query needs dynamic structure, implement it as a handler with explicit sanitization:
```typescript theme={null}
export default async function handler(payload: {
inputs: Record
tool: string
}) {
const { user_id, fields } = payload.inputs
const allowedFields = ['id', 'name', 'email', 'created_at']
const selectedFields = fields.filter((f: string) => allowedFields.includes(f))
// Construct query with validated field list
}
```
### Deploy behind a gateway
Place Hyperterse behind an API gateway or reverse proxy with rate limiting, request size limits, IP access control, and TLS termination.
## Environment variable safety
`{{ env.VAR }}` values are substituted without sanitization. If a variable contains SQL-significant characters and is used in a statement, the same injection risk applies.
Missing variables fail execution rather than defaulting to empty strings.
## Responsibility matrix
| Layer | Protection | Responsibility |
| ------------------------- | ----------------------------------------- | ------------------------------- |
| Input type validation | Type conversion and required checks | Framework (automatic) |
| Credential isolation | Connection strings hidden from callers | Framework (automatic) |
| Auth enforcement | Pre-execution access control | Framework (configured per-tool) |
| String input sanitization | Content validation and format enforcement | Developer (input transforms) |
| Statement safety | Avoiding structural injection | Developer (query design) |
| Network security | TLS, rate limiting, access control | Operator (infrastructure) |
# Production hardening
Source: https://docs.hyperterse.com/security/production-hardening
Security measures, deployment configuration, and hardening for production environments.
This page covers security beyond input validation: network security, authentication enforcement, logging hygiene, container security, and deployment architecture.
## Authentication
Auth is per-tool — there is no global middleware that applies automatically. You must opt in for every tool that handles sensitive data.
### Enforce auth on every sensitive tool
Auth is per-tool. There is no global middleware. Every tool accessing sensitive data must have an `auth` block:
```yaml theme={null}
auth:
plugin: api_key
policy:
value: '{{ env.ROUTE_API_KEY }}'
```
Audit checklist:
* Review all `app/tools/*/config.terse` files for missing `auth` blocks.
* `allow_all` should only appear on health checks and intentionally public tools.
* `hyperterse validate` confirms structural correctness; manually verify auth presence.
### Rotate credentials
API keys and tokens are resolved at startup. Rotation requires a restart. For zero-downtime rotation:
1. Update the secret in your secrets manager.
2. Trigger a rolling restart.
3. Verify the old key is rejected after all instances restart.
## Network security
Hyperterse listens on plain HTTP. Encryption and access control belong to the infrastructure layer in front of it.
### TLS termination
Hyperterse does not terminate TLS natively. Deploy behind a reverse proxy:
* Nginx — `proxy_pass` to `http://localhost:8080`.
* Caddy — automatic HTTPS with Let's Encrypt.
* AWS ALB / GCP LB — managed TLS.
* Kubernetes Ingress — cert-manager or cloud provider integration.
### CORS
Hyperterse applies permissive CORS by default. For production:
1. Deploy behind a reverse proxy.
2. Configure restrictive CORS at the proxy.
3. Restrict built-in CORS to your domain.
### Network segmentation
* Place Hyperterse in a private network segment.
* Restrict inbound traffic to the reverse proxy only.
* Restrict outbound to database hosts and external APIs used by handlers.
* Block direct internet access to the Hyperterse listen port.
## Logging hygiene
Logs are the first place an attacker looks after a breach. Keep them clean.
### Log level
Set production to `2` (warn) or `3` (info):
```yaml theme={null}
server:
log_level: 2
```
Debug-level logging may include substituted statements, input values, and execution details.
### Log routing
Use `--log-file` for file-based collection by aggregation systems. Review handler scripts for accidental credential logging via `console.log`.
## Container security
A smaller container means fewer CVEs and a faster patch cycle.
### Minimal base image
```dockerfile theme={null}
FROM scratch
COPY dist/hyperterse /hyperterse
COPY dist/model.bin /model.bin
COPY dist/build/ /build/
ENTRYPOINT ["/hyperterse", "serve"]
```
Or `alpine` for shell access:
```dockerfile theme={null}
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
RUN adduser -D -u 1001 hyperterse
COPY dist/ /app/
WORKDIR /app
USER hyperterse
EXPOSE 8080
ENTRYPOINT ["./hyperterse", "serve"]
```
### Non-root execution
```dockerfile theme={null}
RUN adduser -D -u 1001 hyperterse
USER hyperterse
```
### Read-only filesystem
Hyperterse does not write to disk at runtime (cache is in-memory):
```yaml theme={null}
securityContext:
readOnlyRootFilesystem: true
```
## Secrets management
Credentials must never appear in configuration files, logs, or version control.
### Environment variables
All credentials must come from environment variables:
```yaml theme={null}
connection_string: '{{ env.DATABASE_URL }}'
```
### Kubernetes secrets
```yaml theme={null}
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
```
### External secrets managers
Use a sidecar or init container to fetch from AWS Secrets Manager, Vault, etc. Signal restarts on rotation. Do not mount secrets as files — Hyperterse reads environment variables.
## Rate limiting
No built-in rate limiting. Implement at the infrastructure layer:
* Reverse proxy — Nginx `limit_req`, Caddy `rate_limit`, Envoy rate limit filter.
* API gateway — AWS API Gateway, Kong, Traefik.
* Cloud provider — Cloud Armor, AWS WAF, Azure Front Door.
Apply to `/mcp`, which handles all tool invocations.
## Health checks
Expose lightweight probes so your orchestrator can detect and restart unhealthy instances.
### Liveness
```bash theme={null}
curl http://localhost:8080/heartbeat
```
Confirms the HTTP server is accepting connections. Does not check connector health.
### Readiness
For full-system readiness, create a tool that queries each adapter:
```yaml theme={null}
# app/tools/readiness/config.terse
description: 'Readiness check'
use: primary-db
statement: 'SELECT 1'
auth:
plugin: allow_all
```
## Deployment checklist
* [ ] All sensitive tools have `auth` blocks.
* [ ] No `allow_all` on tools accessing sensitive data.
* [ ] Credentials via environment variables, not plaintext.
* [ ] `.env` excluded from version control.
* [ ] TLS at reverse proxy or load balancer.
* [ ] CORS restricted to application domain.
* [ ] Log level at 2 or 3.
* [ ] Container runs as non-root.
* [ ] Hyperterse listen port not exposed to the public internet.
* [ ] Rate limiting at infrastructure layer.
* [ ] Health and readiness probes configured.
* [ ] Observability enabled.