agentscope-extensions-jdbc provides full-stack distributed storage over standard JDBC and is the single entry point for relational databases: pass any JDBC DataSource and the dialect is auto-detected via SPI — no per-database setup. A natural fit for teams with existing relational database infrastructure.
Currently supported databases:
Support for more relational databases is on the roadmap, including Oracle and domestic Chinese databases such as DM (Dameng), GaussDB, and OceanBase.
The legacy modulesagentscope-extensions-mysql,agentscope-extensions-postgresql,agentscope-extensions-skill-mysql-repository, andagentscope-extensions-skill-postgresql-repositoryare deprecated and replaced by this module. See migration below.
Dependency
mysql-connector-j, postgresql, sqlite-jdbc).
One-Line Setup
tablePrefix is recommended. The builder also offers storeTableName / sessionStateTableName / snapshotTableName for full per-table overrides, which are rarely needed:
AbstractJdbcDialect.from(ds).build(): autoCreateTable (default true) controls whether DDL is executed, and all enabled business tables are validated either way — a missing table or column fails fast with the reference DDL (in Spring this surfaces at context startup). Table names and prefixes must match [A-Za-z_][A-Za-z0-9_]*.
Table groups: enableBaseTables (default on) and enableSkillTables (default off) decide which groups build() creates and validates. They are independent of autoCreateTable and of each other — base tables on MySQL with skills on the git channel is a valid setup.
agentscope_store, agentscope_sessions, agentscope_snapshots; the lock table agentscope_distributed_locks is created on first lock use. With the skill group enabled: additionally agentscope_skills, agentscope_skill_resources.
Components Provided
1. JdbcAgentStateStore
Agent state persisted to a database table.session_id, state_key, item_index, state_data (LONGTEXT JSON), version, created_at, updated_at, with primary key (session_id, state_key, item_index). The version column backs saveIfVersion optimistic concurrency control (CAS writes).
2. JdbcStore (BaseStore)
Workspace filesystem KV storage. All SQL comes from the dialect layer — the component itself is database-agnostic.putIfVersion uses a single-statement CAS UPDATE ... WHERE version = ?, supported on all databases above.
3. JdbcSnapshotSpec
Sandbox snapshots stored as BLOBs (LONGBLOB on MySQL, BYTEA on PostgreSQL).4. JdbcSandboxExecutionGuard
Distributed lock; the lock strategy is decided by the dialect:- MySQL: native
GET_LOCK()/RELEASE_LOCK(). The lock is tied to the JDBC connection and auto-released on connection close; lock names longer than 64 characters are hashed automatically. - PostgreSQL / H2 / SQLite: a portable lock on the
agentscope_distributed_lockstable, with no database-specific syntax.
Note: MySQL named locks are server-level, not database-level. Use a unique keyPrefix when sharing a MySQL instance.
5. JdbcAgentSkillRepository
Skill storage on the same dialects: implements core’sAgentSkillRepository, so every database this module supports can back the skill channel.
agentscope_skills and agentscope_skill_resources (composite PK (id, resource_path), foreign key with ON DELETE CASCADE). Like the other components, the repository never touches the schema — enable the group at build; constructing it over a dialect without the group fails fast.
Two behaviors to note:
metadata_jsonis a required column. A table from before the column existed fails startup validation with the reference DDL; add the column as the error suggests and restart — the framework never alters existing tables.deleteremoves a skill’s resources explicitly before the row itself, so it behaves the same on SQLite, where the cascade only fires withPRAGMA foreign_keyson. Resource paths must be relative without..— anything escaping the skill directory is rejected on save, and rows read back from the table are validated the same way.
Migrating from Legacy Modules
Continued use of the legacy modules is discouraged — migrate as early as your schedule allows:
Migrating the skill repositories:
- Tables created by the current legacy modules already include
metadata_jsonand work as-is; older tables need the column added first — the startup error carries the reference DDL. - One caveat to “work as-is”: the legacy modules never rejected absolute or
..resource paths, and the new implementation validates rows on read —getSkillrefuses such a row;getAllSkills/getAllSkillNamesskip it with a warning. A row whose name fails validation cannot be deleted either —clearAllSkillsor direct SQL is the only remedy. Auditresource_pathand skill names before migrating; those values were never safely consumable downstream. - The old modules implicitly created an
agentscopedatabase (MySQL) or schema (PostgreSQL). The new repository puts its tables wherever the connection points — aim theDataSourceat the existing tables. databaseName/schemaNamehave no equivalent — the tables live in whatever database the DataSource points to, same as the base tables. Table names can be overridden viaskillTableName/skillResourcesTableName. Correspondingly,getSource()changes frommysql_<databaseName>_<table>/postgresql_<schemaName>_<table>tojdbc_<skillTableName>— consumers keying on it (e.g. the skill staging cache namespace) get a fresh subtree after migration, and the old one is reclaimed by orphan GC.