Skip to main content
Hyperterse v2 is a breaking redesign. The framework shifts from a single-file query gateway to a compiled, filesystem-based MCP framework.

What changed

AspectV1V2
ConfigurationSingle .hyperterse with inline adapters and queriesRoot config + app/adapters/*.terse + app/tools/*/config.terse
Tool mappingQuery name from config keysFilesystem: each tool directory = one tool
ScriptsNoneTypeScript handlers, input/output transforms
AuthNonePer-tool pluggable auth
TransportHTTP query endpointsMCP Streamable HTTP (/mcp)
BuildRuntime interprets config directlybuildmodel.binserve
BundlingNoneScripts bundled + vendor deduplication
CachingGlobal onlyGlobal + per-tool override
ObservabilityBasic loggingOpenTelemetry tracing + metrics + structured logging

Migration steps

Work through the following changes in order. Each step is independent — you can commit after each one.

Keep the root config

Your .hyperterse retains name, version, and server. Remove adapters and queries — those move to dedicated files. Before:
name: my-api
version: 1.0.0
server:
  port: 8080
  log_level: 3
  queries:
    cache:
      enabled: true
      ttl: 60
adapters:
  main_db:
    connector: postgres
    connection_string: '{{ env.DATABASE_URL }}'
queries:
  get-user:
    use: main_db
    statement: 'SELECT * FROM users WHERE id = {{ inputs.user_id }}'
    inputs:
      user_id:
        type: int
After:
name: my-api
version: 2.0.0
server:
  port: 8080
  log_level: 3
tools:
  cache:
    enabled: true
    ttl: 60

Extract adapters

For each v1 adapters entry, create app/adapters/<name>.terse: app/adapters/main-db.terse:
connector: postgres
connection_string: '{{ env.DATABASE_URL }}'
V1 used underscores (main_db). V2 uses the filename (main-db). Update tool use references, or add an explicit name field to preserve the old identifier:
name: main_db
connector: postgres
connection_string: '{{ env.DATABASE_URL }}'

Extract tools

For each v1 queries entry, create app/tools/<name>/config.terse: app/tools/get-user/config.terse:
description: 'Get user by id'
use: main-db
statement: 'SELECT * FROM users WHERE id = {{ inputs.user_id }}'
inputs:
  user_id:
    type: int
    description: 'User primary key'
auth:
  plugin: allow_all

Add authentication

V1 had no auth. V2 requires explicit auth per tool:
  • allow_all for public/health tools.
  • api_key for key-protected tools.
  • Custom plugins for advanced flows.
Do not leave tools without auth unless intentionally unauthenticated.

Add scripts (optional)

If queries need validation or formatting that v1 handled client-side:
mappers:
  input: './validate.ts'
  output: './format.ts'

Install dependencies

If scripts import npm packages:
npm init -y && npm install dayjs uuid
Or with Bun:
bun init && bun add dayjs uuid

Validate

hyperterse validate
Common issues:
ErrorResolution
Tool has neither use nor handlerAdd adapter reference or handler.
Tool defines both use and handlerChoose one execution mode (DB-backed or script-backed).
Adapter name not foundVerify use matches filename or explicit name.
Unknown connector typeUse postgres, mysql, mongodb, or redis.
Script file not foundVerify paths are relative to tool directory.

Test

hyperterse start --watch
Verify tools in tools/list:
curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq

Build for production

hyperterse build -o dist

Update client integration

V1 clients using /query/{name} must migrate to MCP tools/call: Before (V1):
curl -X POST http://localhost:8080/query/get-user \
  -H "Content-Type: application/json" \
  -d '{"inputs": {"user_id": 42}}'
After (V2):
curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "get-user",
      "arguments": {"user_id": 42}
    },
    "id": 1
  }'

Directory structure comparison

V1:
my-api
.hyperterse
V2:
my-api
.hyperterse
package.json
app
adapters
main-db.terse
tools
get-user
config.terse
validate.ts
format.ts
list-orders
config.terse

Breaking changes

ChangeImpactAction
Inline adapters removedConfig will not parseExtract to app/adapters/*.terse
Inline queries removedConfig will not parseExtract to app/tools/*/config.terse
/query/{name} removedHTTP clients breakMigrate to MCP tools/call
Auth now per-toolTools unauthenticated by defaultAdd auth blocks
DSL parser deprecatedText-format blocks unsupportedConvert to YAML
Build step introducedDirect interpretation replacedAdd build + serve to pipeline