Skip to main content
Glama
rezazadeh93

Hermes DB

by rezazadeh93

Hermes DB (MCP + Postgres)

Lightweight containerized MCP server backed by local Postgres so a Hermes agent can store university-program research. Hermes connects over HTTP, not stdio.

flowchart LR
    Hermes[Hermes Agent] -->|streamable HTTP| MCP[hermes_mcp container]
    MCP -->|psycopg| PG[hermes_db container]
    User[Browser] -->|HTTP 8080| Web[hermes_web container]
    Web -->|static files| React[React SPA]
    Web -->|/api JSON| PG

Prerequisites

  • Docker Desktop

  • uv (for local server edits / testing)

Related MCP server: RagLit MCP Server

Start the stack

docker compose up -d

First start runs sql/init.sql (table, CHECKs, indexes, updated_at trigger). Postgres data lives in the hermes_pg_data volume.

For existing Postgres volumes, apply the marker/visit tracking migration before starting the web service:

docker compose cp sql/migration_001_tags.sql postgres:/tmp/migration_001_tags.sql
docker compose exec postgres psql -U hermes -d hermes_db -f /tmp/migration_001_tags.sql

Wait until both services are healthy:

docker compose ps

Resource caps: 384 MB Postgres + 512 MB MCP + 256 MB web, 0.75/0.75/0.5 CPU (~1.15 GB RAM total).

Web UI

A lightweight React + TypeScript SPA is served by the Flask web service on port 8080. After starting the stack:

  • List page: http://127.0.0.1:8080 — filter, sort, and paginate programs.

  • Detail page: click any row to see the full program record.

Supported filters on the list page:

  • Free-text search (university or program_name)

  • research_status and eligibility_status

  • country (with autocomplete from existing rows)

  • min_overall_fit (0–100)

Sortable columns: overall_fit, university, program_name, country, application_deadline, created_at, id.

Persistent markers

Clicking a program marks it as visited and tints the row blue. You can also tag programs with:

Marker

Button

Tint

Meaning

snooze

Snooze

amber

Must see again

important

Important

rose

High priority

want_to_apply

Apply

green

Plan to apply

Markers are stored in the program_tags Postgres table and persist across sessions.

Wire Hermes (streamable HTTP)

Hermes connects to the running MCP container at http://127.0.0.1:8765/mcp:

mcp_servers:
  hermes-db:
    url: "http://127.0.0.1:8765/mcp"

or if your Hermes/Cursor config uses JSON:

{
  "mcpServers": {
    "hermes-db": {
      "url": "http://127.0.0.1:8765/mcp"
    }
  }
}

The server stays up in Docker. Hermes treats it as a remote MCP endpoint.

Tools

Tool

Purpose

list_programs

Compact list. Filters: research_status, country, min_overall_fit, q. Default 20 rows, max 50. Does not return curriculum text.

get_program

Full row by program_id.

add_program

Insert. Duplicate program_url returns the existing id (already_exists: true).

update_program_fit

Patch backend_fit / overall_fit and optional notes. Does not mark verified.

mark_program_verified

Sets research_status = verified and last_verified_at = now().

Local server testing (no Docker)

If you change server.py and want to test before rebuilding:

uv sync
copy .env.example .env   # edit DATABASE_URL to 127.0.0.1 if needed
uv run python server.py

This launches stdio by default. For local HTTP:

$Env:DATABASE_URL="postgresql://hermes:hermes@127.0.0.1:5432/hermes_db"
uv run python server.py

You must have Postgres running (e.g. from docker compose up postgres -d).

Web UI only (no Docker)

To develop the React frontend locally:

# Terminal 1: run the Flask API
uv sync
$Env:DATABASE_URL="postgresql://hermes:hermes@127.0.0.1:5432/hermes_db"
uv run python web.py

# Terminal 2: run the Vite dev server
cd frontend
npm install
npm run dev

Vite proxies /api calls to Flask. Open http://127.0.0.1:5173.

To run just the Flask API and built static files without Vite:

cd frontend
npm install
npm run build
cd ..
$Env:DATABASE_URL="postgresql://hermes:hermes@127.0.0.1:5432/hermes_db"
uv run python web.py

Then open http://127.0.0.1:8080.

Rebuild after edits

docker compose up -d --build

To rebuild only the MCP service:

docker compose up -d --build mcp

To rebuild only the web UI service:

docker compose up -d --build web

Inspect the database

docker compose exec postgres psql -U hermes -d hermes_db -c "\d programs"

Manage Postgres with pgAdmin

A lightweight pgAdmin container is included. After starting the stack, open it at http://127.0.0.1:5050:

  • Email: admin@hermes.dev

  • Password: admin

Then add a new server with:

  • Host: postgres

  • Port: 5432

  • Database: hermes_db

  • Username: hermes

  • Password: hermes

Available Tools

5 tools
add_programB

Insert a program. If program_url already exists, return that id without inserting.

ParametersJSON Schema
NameRequiredDescriptionDefault
cityNo
intakeNo
countryNo
universityYes
backend_fitNo
degree_typeNo
overall_fitNo
program_urlYes
source_urlsNo
degree_levelNo
program_nameYes
subject_areaNo
uncertaintiesNo
research_statusNodiscovered
application_startNo
curriculum_summaryNo
eligibility_statusNounknown
english_requirementNo
academic_eligibilityNo
application_deadlineNo
work_experience_requirementNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It usefully discloses idempotency semantics (existing program_url short-circuits the insert and returns the existing id). It says nothing about required fields, defaults (research_status='discovered', eligibility_status='unknown'), permissions, or what happens with partial/duplicate data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero waste, with the idempotency rule front-loaded immediately after the action statement. Nothing to trim.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, but a 21-parameter write tool with no annotations and no schema descriptions needs far more than two sentences. Required-field guidance, defaults, and failure/duplicate handling are all absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

21 parameters at 0% schema description coverage, and the description explains meaning for only one of them (program_url as the uniqueness key). The remaining 20 fields, including the required university and program_name, get no added semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource: 'Insert a program', and it names the dedup key (program_url). It does not explicitly differentiate itself from siblings like update_program_fit or get_program, but the operation is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use guidance or mention of alternatives. The dedup conditional ('if program_url already exists, return that id') is a behavioral rule, not routing advice, so the agent gets no help deciding between this and update_program_fit or get_program.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_programB

Return the full program row by id.

ParametersJSON Schema
NameRequiredDescriptionDefault
program_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. 'Return' implies a read-only fetch, but it says nothing about permission requirements, behavior when the id does not exist, or whether results are cached — significant omissions for an unannotated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with the resource and lookup key front-loaded and zero filler. Nothing is padded or redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value details are not needed, and the tool is simple (one param, no nesting). Still, with no annotations and no error/not-found behavior described, the definition is only minimally adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description must compensate for the single integer program_id. 'By id' does clarify that the parameter is the program's identifier, but adds no format, range, or source-of-value detail beyond that.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Return the full program row by id'), which is unambiguous and distinguishable from the write siblings (add_program, update_program_fit, mark_program_verified). It does not explicitly contrast itself with list_programs, so sibling differentiation is left implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'by id' implies a single-record lookup, but there is no explicit when-to-use statement, no mention of list_programs as the alternative for multiple records, and no prerequisites. The agent must infer the routing from the sibling names alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_programsA

List programs as compact rows (no curriculum text). Filter by status, country, min fit, or name search.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNo
limitNo
countryNo
min_overall_fitNo
research_statusNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does disclose a real behavioral trait: results are compact rows with curriculum text omitted. It stays silent on pagination behavior (despite the limit param), ordering, and permissions, so coverage is partial.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, no filler, with the output shape front-loaded ahead of the filter list. Nothing could be trimmed without losing content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-field detail is not required. For a five-parameter, zero-required list tool the description is close to sufficient, losing only a point for leaving limit and filter value syntax undocumented.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it partially does by mapping four parameters (research_status, country, min_overall_fit, q) to plain-language filters. It never mentions limit, and gives no syntax or accepted values for the status filter or the fit threshold's range.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List programs') and adds scope detail ('compact rows, no curriculum text') that distinguishes it from the singular get_program sibling. It does not name alternatives explicitly, but the list-vs-get distinction is inferable from the name pair.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Filter by status, country, min fit, or name search' implies the common usage contexts and enumerates the filter axes. However, it gives no guidance on when to reach for a filtered list versus opening a single program with get_program, nor any exclusion criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

mark_program_verifiedB

Set research_status to verified and stamp last_verified_at.

ParametersJSON Schema
NameRequiredDescriptionDefault
program_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the behavioral burden alone. It does disclose the two side effects (status set to verified, timestamp stamped), which is useful, but omits whether this is irreversible, whether it requires elevated permissions, or whether repeated calls are idempotent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single compact sentence with no wasted words, and the state change is front-loaded. It is efficient, though it sacrifices any routing or precondition content for brevity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description covers the mutations performed. However, for a state-changing tool with no annotations, missing permission, idempotency, and parameter-identity details leave it only minimally complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage and the description never mentions program_id or what it refers to, leaving the sole parameter entirely undocumented. With one required parameter and no schema-level explanation, the description fails to compensate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('set') and names the exact state change and field ('research_status to verified', 'last_verified_at'), so the operation is clear without opening the schema. It does not, however, distinguish itself from siblings like update_program_fit, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to call this versus update_program_fit or other state-changing siblings, and no preconditions or alternatives are named. The agent must infer that this is the verification-specific path.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_program_fitA

Patch fit scores and optional research notes. Does not change verification status.

ParametersJSON Schema
NameRequiredDescriptionDefault
program_idYes
backend_fitNo
overall_fitNo
uncertaintiesNo
curriculum_summaryNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full disclosure burden. 'Patch' correctly implies partial-update semantics and the verification-status exclusion is a genuine behavioral boundary, but there is no mention of permissions, reversibility, or what happens to fields omitted or passed as null — a real concern given every optional field defaults to null.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with zero filler, and the core action is front-loaded ahead of the scoping exclusion. Every clause contributes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described. Still, for a 5-parameter mutation tool with 0% schema description coverage and no annotations, the description leaves the patch/null semantics and permission requirements unstated, which is a meaningful shortfall.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must supply param meaning; it does so only at the group level, mapping 'fit scores' to backend_fit/overall_fit and 'research notes' to uncertainties/curriculum_summary. It adds nothing about valid ranges for the integer scores or the null/nullable semantics, which is the most actionable gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('patch fit scores and optional research notes') and explicitly carves out an adjacent behavior ('Does not change verification status'), which distinguishes it from the mark_program_verified sibling. It never names the target entity explicitly, but program_id and the sibling set make it inferable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The negative clause 'Does not change verification status' implies routing to mark_program_verified rather than this tool, which is useful contextual guidance. However, there is no positive when-to-use statement, no prerequisites, and no explicit naming of the alternative, so usage is only partially implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedadd_program
    • First observedget_program
    • First observedlist_programs
    • First observedmark_program_verified
    • First observedupdate_program_fit

TDQS

A3.5/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct operation on the program resource: list, get, add, update fit scores, and mark verified. No overlap between update_program_fit and mark_program_verified as the latter specifically handles verification status while the former handles fit scores and research notes.

Naming Consistency5/5

All tools follow a consistent snake_case verb_noun pattern (list_programs, get_program, add_program, update_program_fit, mark_program_verified). The naming is predictable and clearly indicates the action and target resource.

Tool Count4/5

Five tools is a reasonable set for a database management server focused on programs. It covers the core operations without feeling bloated, though it is on the lower end of the typical 3-15 range.

Completeness3/5

The surface includes list, get, add, and two update operations, but lacks a general update tool for fields beyond fit scores and a delete tool. This may cause agents to hit dead ends when needing to remove or broadly edit programs.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers