The sink ships with a JSONB variant and is documented for asyncpg, but
the suite only ever exercised SQLite. The embeddings work will need
pgvector, so this adds a local Postgres stack and a way to run the
database tests on it, here and in CI.
- docker-compose.yml: ffb-postgres (pgvector/pgvector:pg16) on a private
network, published on 127.0.0.1 only, with an init script that adds
the vector extension to the test database and template1
- Makefile: pg-up/pg-down/pg-reset/pg-logs/pg-psql, test, test-pg,
test-all, lint; .env is generated from .env.example with a random
password, and FFB_TEST_DATABASE_URL from the environment wins over it
- tests: a db_url fixture parametrized over sqlite and postgres. The
postgres leg skips unless FFB_TEST_DATABASE_URL is set, and creates a
uniquely named database per test (with vector) that is dropped after.
The DatabaseSink factories in the events, retention, correlation,
soft-error and instrumentation tests now use it, so their 38 database
tests run on both. The missing-events-table test accepts either
backend's error. New tests check isolation and that vector works.
- asyncpg and pgvector added to the dev and test extras
- CI: a test-postgres job on FastMCP 2.12.2 and 4.x with a pgvector
service container, failing if the Postgres cases skip
- CONTRIBUTING: how to run the suite against Postgres
Changelog entry and version bump for linking feedback items to the tool
calls that preceded them. README documents the instrumentation parameter
of add_feedback_tools and the new feedback_call_links table that Alembic
hosts need to migrate.
InstrumentationMiddleware (and instrument(app)) records every tool call on
a FastMCP server: tool, timing, outcome and original error type, session
and request ids, argument and result sizes, plus identity and enrichment
hook output. Modes off/meta/full; full adds redacted args and results.
Recording can't slow or break a call. Records reach sinks through a
bounded queue drained by a background task and are dropped, counted, when
it fills. Sink and hook failures are logged, never raised, and async hooks
are cut off after hook_timeout.
Redactor strips secrets by key at any depth and by value pattern from
args, results, error text and hook output. DatabaseSink runs on async
SQLAlchemy, takes a host AsyncEngine, writes to a prefixed table and never
creates it unless asked; build_metadata() plugs into Alembic env.py.
Verified on FastMCP 2.12.2, 2.14.3, 3.3.1, 3.4.7 and 4.0.10. CI matrix
gains 3.x. Version 2026.09.27.1.
compose_feedback_services() called import_server() without awaiting it.
That method became async in FastMCP 2.14, so on the version this package
was built against it composed nothing while logging success. FastMCP 4
removed it outright. Use mount(), which is sync on 2.12 through 4.x.
The test suite could not start (pytest-html hook, plugin not installed),
so nothing caught this. Add pytest-html, bring examples/ over from the docs
repo since the integration tests import them, and fix the tests that had
drifted from the code. 407 tests pass on FastMCP 2.12.2, 2.14.3 and 4.0.10.
- Declare fastmcp>=2.12.2,<5; CI matrix covers each family plus a
non-blocking canary on the newest FastMCP
- Add a composition test with real servers; the mocked ones could not
see a missing await
- Rewrite the README against the actual API and execute its examples
- Ignore ruff UP042: StrEnum would change stored enum formatting
- Version 2026.09.27
The mixin modules import shared types from base.py, but they
weren't being re-exported. Add __all__ with explicit re-exports for:
- Feedback, FeedbackStatus, FeedbackType (from models)
- feedback_to_dict (from models)
- mcp_tool (from fastmcp)
Bump version to 2026.01.12.1