Hermes DB
Hermes DB exposes a Postgres-backed MCP toolset for storing, searching, and tracking university-program research for a Hermes agent.
Search/list programs —
list_programsreturns compact rows (20 default, 50 max, no curriculum text), filterable by free-textq(university or program name),country,research_status, andmin_overall_fit.Retrieve full details —
get_programfetches the complete program row byprogram_id, including curriculum text.Add new programs —
add_programinserts a record (required: university, program_name, program_url) plus optional metadata like degree level/type, subject area, deadlines, eligibility, English and work-experience requirements, fit scores, uncertainties, and source URLs; duplicateprogram_urlreturns the existing id withalready_exists: true.Update fit scoring —
update_program_fitpatchesbackend_fit,overall_fit,uncertainties, andcurriculum_summarywithout affecting verification status.Mark verified —
mark_program_verifiedsetsresearch_status = verifiedand stampslast_verified_at.
Note: the MCP server only covers program data. Visit/interaction markers (snooze, important, want_to_apply) are handled through the web UI's /api layer, not by MCP tools.
Backs the MCP server with a local Postgres database that stores university-program research. The tools let an agent list and filter programs (by research status, country, minimum overall fit, or free-text query), fetch a full program record by id, insert new programs (de-duplicating on program URL and returning the existing id), patch backend/overall fit scores and notes, and mark a program as verified with a verification timestamp.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Hermes DBlist programs in Germany with overall fit at least 80"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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| PGPrerequisites
Docker Desktop
uv (for local server edits / testing)
Related MCP server: RagLit MCP Server
Start the stack
docker compose up -dFirst 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.sqlWait until both services are healthy:
docker compose psResource 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 (
universityorprogram_name)research_statusandeligibility_statuscountry(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 | amber | Must see again |
| Important | rose | High priority |
| 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 |
| Compact list. Filters: |
| Full row by |
| Insert. Duplicate |
| Patch |
| Sets |
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.pyThis launches stdio by default. For local HTTP:
$Env:DATABASE_URL="postgresql://hermes:hermes@127.0.0.1:5432/hermes_db"
uv run python server.pyYou 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 devVite 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.pyThen open http://127.0.0.1:8080.
Rebuild after edits
docker compose up -d --buildTo rebuild only the MCP service:
docker compose up -d --build mcpTo rebuild only the web UI service:
docker compose up -d --build webInspect 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.devPassword:
admin
Then add a new server with:
Host:
postgresPort:
5432Database:
hermes_dbUsername:
hermesPassword:
hermes
Available Tools
5 toolsadd_programB
Insert a program. If program_url already exists, return that id without inserting.
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | ||
| intake | No | ||
| country | No | ||
| university | Yes | ||
| backend_fit | No | ||
| degree_type | No | ||
| overall_fit | No | ||
| program_url | Yes | ||
| source_urls | No | ||
| degree_level | No | ||
| program_name | Yes | ||
| subject_area | No | ||
| uncertainties | No | ||
| research_status | No | discovered | |
| application_start | No | ||
| curriculum_summary | No | ||
| eligibility_status | No | unknown | |
| english_requirement | No | ||
| academic_eligibility | No | ||
| application_deadline | No | ||
| work_experience_requirement | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| program_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | ||
| limit | No | ||
| country | No | ||
| min_overall_fit | No | ||
| research_status | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| program_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| program_id | Yes | ||
| backend_fit | No | ||
| overall_fit | No | ||
| uncertainties | No | ||
| curriculum_summary | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
5 tool updates
v0.1.0- First observed
add_program - First observed
get_program - First observed
list_programs - First observed
mark_program_verified - First observed
update_program_fit
TDQS
Scored across 5 tools
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.
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.
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.
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
Related MCP Connectors
Multi-engine scholarly research server for search, traversal, full text, and reading lists.
Versioned agent memory in your own Postgres: portable context, permissioned, audit trail.
PostgreSQL, MySQL, OpenAPI/Swagger, and shared Agent Memory with scoped access.
Shared memory for AI agents, as a graph in your own Postgres. Writes never call an LLM.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceServer for managing academic literature with structured note-taking and organization, designed for seamless interaction with Claude. Built with SQLite for simplicity and portability.19MIT
- FlicenseCqualityDmaintenanceEnables AI agents to ingest documents, generate embeddings, and perform semantic search via PostgREST APIs.3-
- AlicenseNot gradedqualityDmaintenanceProvides tools for semantic search, CRUD, verification, and reindexing of documents in a local vector-based knowledge base, usable via HTTP or stdio.MIT
- AlicenseNot gradedqualityCmaintenanceA Postgres-backed bookmark manager that exposes CRUD tools, database inspection, and a configurable REST API tool to save, search, and manage bookmarks via natural language.MIT