Repository navigation
Releases
v0.8.4
Fixed
- agents: namespaces written by 0.8.2 keep their people after the upgrade.
Namespaces now list people by e-mail address, but a record 0.8.2 wrote names
its creator and members by user id — and those entries matched nobody, so
everyone lost access to their namespaces, with no way to repair it in the
UI. An entry without an@is read as a user id again and matches the
account that signs in with it. Likewise,mindconnect.namespace-adminsset
on an installation whose default namespace already had a record added
nobody to it and locked everyone out; the named admins are now added to the
existing record.
What's Changed
Full Changelog: v0.8.3...v0.8.4
v0.8.3
Added
- agents: a module can add its screen to the admin UI's sidebar. A jar on
the classpath that serves a page of its own had no way into the navigation —
the menu was a fixed list, so the page was reachable by URL only. It now
registers anAdminMenuContributionbean and says which entries to show,
for an admin of the namespace and for a plain user of it separately, so a
contribution never offers a viewer a route the server would refuse. An entry
is a link or a group of links, like Install; groups with one id from several
modules become one. The entries follow the shipped sections, before the
Install group. - agents: a module on the classpath ships its own agents, skills and workflows. The
seeds were read from the app's own jar only:initial-data/in a second jar
was never scanned, so a module that brings an agent had to install it by
hand. The loaders — and the Migrations screen — now look through every jar
on the classpath, so a module'sinitial-data/agent-definitions/*.json,
skills/*.mdandworkflows/*.jsonare seeded and migrated exactly like the
bundled ones. - agents: a turn that waits for an approval can say so. A tool marked
needsApprovalused to leave the caller waiting for the final answer until
somebody answered the card elsewhere.AgentRuntime.send(…)returns as soon
as a call waits, with aTurnResultthat isINCOMPLETEand lists the open
questions;approve(…)anddeny(…)continue the same turn and return its
answer or the next question. Underneath,ChatTurnHandle.outcome()completes
at the question, andAgentChatService.sendChat,approveanddenyare the
non-blocking forms. Over REST the chat stream ends a waiting stretch with an
incompleteframe, andPOST /api/sessions/{id}/approvals/{callId}/continue
answers and streams the turn on. The runtime protocol backend ends such a
responseINCOMPLETE(WAITING_FOR_APPROVAL)with anApprovalRequestitem,
and anApprovalResponseas the next input continues the turn. - agents: the Admin UI can carry your own name, mark and stylesheet.
mindconnect.branding.*sets the heading in the header, the logo beside it
and where it leads, the browser tab's title and icon, the look the shell
opens in, and any number of stylesheets that are linked after the shipped
ones so their rules win.assets-dirserves a directory next to the running
app at/branding/**, so an installation brands itself by dropping files in
rather than by building its own image; the login page follows the same
settings. Unset, everything looks exactly as before — except the browser tab,
which now says "Mindconnect Agent Runtime" instead of "Agent Admin".
switchtakes it one step further: named variants with aurl-patterneach,
so one deployment behind two host names wears two brands, decided per request
rather than per process.style-pickersays what the header's theme picker
may offer — every shipped theme, a named few, or none at all, and with none
the look is genuinely fixed. See
Branding the app. - agents: the calendar is a port too, and CalDAV comes with it.
mc-calendar-corehasCalendarStore— the calendars, a period of
appointments, reading one, creating, changing and deleting — and the
CalendarProviderseam a kind of account plugs into.mc-calendar-caldav
brings CalDAV, which nearly every mail provider and every self-hosted
calendar speaks: a card with the CalDAV address, user and password, a Test
button that signs in and says which calendars it found, and as much
iCalendar as an appointment needs.mc-agent-tools-calendarbrings
calendar_calendars,calendar_events,calendar_read,calendar_create,
calendar_updateandcalendar_delete, which name an account
provider.key(caldav.web) or askall— one set of tools whatever kind
of calendar a user connected, and each change a tool of its own so a binding
can make it ask for approval. - agents:
code_executeruns python in an image with the office libraries.
The default python image is nowghcr.io/mindconnect-ai/code-exec-python:
Python 3.12 with python-pptx, python-docx, openpyxl, matplotlib and pandas, and
themc_officepackage whosepptx_builder,docx_builderandxlsx_builder
build a deck, a Word document or a workbook from a small spec. The office
skills used to carry those generators as text the model had to copy into every
call — tens of kilobytes that crowded a local model's context before the first
slide was written. The tool tells the model what the image carries. It is
published for amd64 and arm64;mindconnect.code-exec.languages=python=python:3.12-slim
goes back to the plain image. - agents: a connection made by signing in is named after the account. After
"Connect with Microsoft" the card said "Microsoft account", and with two of them
nobody could tell which was which — nor which mailbox a mail would leave from. The
connection is now tried out the moment it arrives, and when the source's test says
whose account it is, the connection takes that name: the address, as a rule. The
toast after signing in says what the test found. AConnectionTestcan carry the
accountit saw; sources that do not say leave the card's name in place. - agents: a connection can be tried out from the Connections page. A
tool source that declares aConnectionSpecmay offer aConnectionTester
(connectionTester()onToolFactoryandMultiToolProvider); the page
then shows a Test button on each of that provider's rows, runs the test
right after a connection is added or edited — while the person who typed
the password is still looking — and writes the verdict into the Status
column, so a wrong password reads "error — the server refused the account"
instead of every tool call failing the same way. An OAuth token is renewed
before the test, as it would be before a tool call.ToolRegistry
gainedconnectionTesterOf(provider). - agents: a jar on the classpath declares what it brings, and a namespace can switch it off.
A module that ships tools, agents or a screen was loaded by whatever
ServiceLoaderfound, with no record of what it brought and no way to keep
it out of one namespace. It now carries a manifest,
META-INF/mindconnect/extension.json— id, vendor, version, and under
contributesits tools (as name patterns), agents, menu entries, routes,
decorated ports and more. The host reads every manifest at start and the
new Extensions screen (Install → Extensions) lists them; an admin
switches an extension off for the namespace at hand, and its tools leave
the catalogs and its sidebar entries the menu there. From a brand's own
namespace the decision can be taken for the whole brand, locked against
the namespaces' own say or not;mindconnect.extensions.disabledlets the
operator switch an extension off everywhere. Decisions are stored per
namespace (system/extensions/on files,mc_extension_activationin
Postgres) and per brand (system/extension-brands/,
mc_extension_brand_activation). Menu entries a manifest declares with a
label and href are rendered by the host, into a shipped group or a new
one; the routes it declares are sections of the admin UI — shell, layout
and a 404 where the extension is off — so a jar's own screen needs no
filter of its own; and beside every declared tool pattern and provider
class the screen says whether the classpath actually has it. A worked
example ships with the admin UI app, switched off:mc-extension-demo, a
short adventure with the LLM as game master — a dice tool, an agent, a
decorator around the trace store, a store of its own for scenarios, saved
games and rolls, and two screens; switch it on under Install → Extensions. Problems — two manifests with one id, an unreadable one, two
extensions replacing the same bean — and jars that bring providers without
a manifest are logged and shown;mindconnect.extensions.strict=true
refuses to start on them instead. - agents: the chat's Files dialog opens, edits and deletes files. A file
opens in place of the tree: text up to 1 MB in an editor with Save, images
scaled to fit, PDFs in the browser's viewer; Office documents and other
binaries are offered as a download. Files and folders can be deleted, a folder
with everything in it, after a confirmation; a directory of the chat itself
never can, and a link is removed without touching what it points to. Behind
it areGET …/files/view,POST …/files/saveandPOST …/files/deleteunder
/admin/api/sessions/{id}, for the session's owner only. Workspaces on a
virtual environment server work the same way;WorkspaceFilesgained
delete(Path). - agents: a folder in the chat's Files dialog downloads as a zip. Every
folder, each of the chat's directories included, has a download button that
packs everything below it into one zip, instead of fetching file by file. The
zip holds only what the dialog would list, so a link out of the directory
stays out. A folder with more than 10,000 files or more than 1 GB is refused
(413). Behind it isGET /admin/api/sessions/{id}/files/zip?root=…&path=…,
for the session's owner only, like the rest of the dialog. - agents: A folder's newest thousand messages are kept, and a list or a search answers from there.
mc-mail-indexholds a window per folder somebody looks at — headers only, in a file per user, account and folder — filled on the first look, compared with the provider's top page when older than two minutes, widened when a page below it is read, and written through by every delete, move and read flag. The folder views and a gathered list read from it; `m...
Snapshot main · 0.8.3-SNAPSHOT (2026-09-23)
Version 0.8.3-SNAPSHOT, built from main at 4d4a563.
Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.
curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot/mc-agent-admin-ui-app-0.8.3-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.8.3-SNAPSHOT-exec.jarThe CLI is mc-agent-cli-0.8.3-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.
v0.8.2
Added
-
agents: namespaces — a server now serves any number of them, and a
user works in one at a time. Everything lives inside a namespace (agents,
LLM configs, skills, sessions, files, vector stores, workflows, MCP servers,
tool settings, MCP registrations); only users, API tokens and the namespaces
themselves are installation-wide. Workflows, MCP servers and vector stores
follow the namespace of the request like every other store; a new namespace
starts without any of them (the bundled MCP registrations are seeded into the
default namespace only). The helper agents a namespace needs — the title
generator, the summarizers — are created there on first use, on the
namespace'sagent-default(or first) LLM config. A chat's title is now
generated by a task of its own (agent.title), submitted together with the
first turn and run in parallel with it from the user's message alone: it
shows in the task monitor, survives a restart, runs in the turn's namespace,
and the turn's handle resolves once both are done — so the title is there
when the answer is. The task monitor — the header badge, its
dialog and the live updates — shows the tasks of the namespace you are in. In the Admin UI the header shows the current namespace:
switch from the menu, create a new empty one and fill it from the Registry,
or open Namespaces & members to see who works where. Your profile lists
the namespaces you may work in, with Switch to and Invite… on each row;
the invite dialog takes the other user's user name (someone who never
signed in is refused). Only the creator of a namespace invites, removes members, renames
it — and deletes it, with everything in it: the directory under the data
directory, or every row of it in Postgres and its pgvector tables. Any
other member can leave. Whoever loses a namespace this way is back in the
default one. A namespace cannot be calledsystem,ns,api,adminor
v1. The header switch is a POST. Fixes from a review of the branch: the
live task stream no longer shows a namespace its viewer was removed from;
the working-directory root follows the namespace of the call instead of
the one at start-up; a turn waits at most three hours again; MCP tool catalogs and pooled
connections are kept per namespace, so editing a server in one namespace
no longer drops another's connections; a malformedX-Mindconnect-Namespace
header is a 400. Two users creating a namespace with the same id at the
same moment get one namespace, not the second record over the first; and
a store's embedding config is always read in the store's own namespace.
A call to the REST API or the Responses API without a
namespace works in the default one — never in the one its user last chose
in the Admin UI; only the Admin UI follows the session and the remembered
choice. The choice is
remembered per user, so it survives a new browser session and a restart. The default namespace
(mindconnect.namespace,local) is open to every signed-in user, so an
installation that never creates another behaves as before. API clients name
the namespace in the address —https://host/ns/acme/api/…and, for
OpenAI-compatible clients, the base URLhttps://host/ns/acme— or in an
X-Mindconnect-Namespaceheader; the server answers with the namespace it
worked in under the same header, and with403when the caller is not a
member. Embedders see none of this: the builder still takes one namespace.
Storage moved: users and API tokens now live under
<data.base-dir>/system/(Postgres: without anamespacecolumn) instead of
insidelocal/; on an existing install sign in again and re-issue tokens.
No migration is shipped: a Postgres database from 0.8.0 or 0.8.1 still
hasmc_userandmc_api_tokenwith the namespace in their primary key,
and the new code cannot write to those tables — every sign-in record,
namespace switch and token creation fails. Drop both tables before starting
this version (DROP TABLE mc_user, mc_api_token;); they are recreated
empty, users reappear on their next sign-in. -
agents: the model's reasoning is shown in the chat — as a collapsed
card above the answer, like a tool call, whose header runs while the model
thinks and reads "thought for 4.2 s" once the first token arrives. Open it
to read along. A Qwen3, DeepSeek-R1 or gpt-oss model on LM Studio, Ollama,
vLLM or Groq used to leave the chat sitting on "AI is thinking" with nothing
to show for it — the OpenAI-compatible gateway now picks the reasoning up
from thereasoning_contentandreasoningfields as well as from inline
<think>tags in the content (which no longer leak into the answer). The
card comes back on reload, duration included: the message keeps its
reasoning inmetadata.thinkingandthinkingMs, on an answer and on a
tool-call turn alike. The REST stream carries it as athinkingframe, the Responses API as areasoning
item withresponse.reasoning_summary_text.deltaevents; the CLI stays
quiet. Claude's adaptive thinking shows the same way when the config asks
for it (thinkingDisplay: summarized). -
agents:
reasoning_efforton every OpenAI-compatible LLM config
(OpenAI, Azure, Groq, xAI, OpenRouter, Ollama, LM Studio, …) — a select in
the Admin UI form next to the provider's other parameters, or
"additionalParams": {"reasoning_effort": "high"}in the config JSON. Which
levels apply depends on the model. Local servers ignore the field; OpenAI
is only sent it with a reasoning model (gpt-5, o-series), since it rejects
it on any other. -
agents: an LLM config can name fallback models. When the provider
rate-limits it (HTTP 429) or reports itself overloaded (529) and the config's
own retries are used up, the same request is re-sent through the next config
infallbackModels, in order, until one answers — normally a config at
another provider, whose limit is a different limit. Configure it in the Admin
UI (Fallback models (on rate limit) in the chat settings: tick chat
configs and move them into the order they are tried) or as a
"fallbackModels": ["openai-default", "gemini-default"]list in the config
JSON. Omitting it keeps the previous behaviour: the rate-limit error reaches
the caller. A fallback is only taken before anything has streamed, so a
partial answer is never duplicated. -
agents: two providers more —
XAI(the Grok models) and
MOONSHOT(the Kimi models). Both speak the OpenAI API, so they arrive
with everything the others have: endpoint prefilled (https://api.x.ai,
https://api.moonshot.ai— set the base URL tohttps://api.moonshot.cnfor
Moonshot's mainland-China endpoint), model list in the form, retry, rate
limit and fallbacks. The provider dropdown now names the models a vendor is
known for, so Grok and Kimi are findable under XAI and MOONSHOT. The apps
register the OpenAI-compatible gateway as the default for every provider
rather than listing them one by one, so the next one is a single enum
constant. -
agents: the Admin UI's LLM-config form reads the model list from the
provider, so a model is picked rather than typed. LM Studio already did
this through its native API; now every provider that publishes a listing does
— OpenAI, Groq, Mistral, DeepSeek, Together, OpenRouter, Perplexity,
Fireworks and Ollama through/v1/models, Anthropic and Gemini through their
equivalents. Only models fitting the config type are offered — image,
text-to-speech, moderation and legacy completion models are left out — a
model the provider no longer lists stays selectable, and a listing that fails
leaves a text field carrying the reason. OpenRouter's list needs no API key.
Azure OpenAI keeps free text: its configs name a deployment, not a model. -
agents: registries — a Registry screen that installs LLM configs,
agents, workflows and whole packages from a GitHub project. A registry is a
repository, not a server: an index (registry.json) and the entity files it
points at, added by itsowner/repo(owner/repo@v1.2.0pins a tag, a
private one names the environment variable holding its token). Browse it
under Agents, LLM configs, Workflows and Packages, import — a package
installs the agent, its sub-agents, the workflow they run and the LLM config
they share, dependencies first and each entity once, with a report that says
line by line what was imported, updated, removed, skipped or failed. A
package's Contents tab lists everything it would install, marks what is
already here, and lets you untick entries to leave them out; Remove takes
a package out again, deleting the included entries and keeping the rest. An
entry that something outside the package still uses — the alias every agent
runs on, the config behind it — says Used by … and starts unticked.
Importing keeps what is already here unless overwrite is chosen, an overwrite
keeps local ids so that sessions and aliases keep working, and an API key
that came with a registry file is dropped rather than used. An application
ships registries as files underinitial-data/registries/; the admin UI
comes with mindconnect-ai/mc-registry,
the default agents, LLM configs and workflows plus an example package. Off
for an installation that wants no outbound calls
(mindconnect.registry.enabled=false); format and settings in the docs under
Registry. -
agents: skills — know-how written down once and loaded when it is needed,
instead of carried in a system prompt from the first token on. Only a skill's
name and description stand in the prompt, a line each; the instructions
arrive when the model calls the newskilltool for that name, so ten skills
cost ten lines until one is use...
Snapshot feature/namespace-supplier · 0.8.2-feature-namespace-supplier-SNAPSHOT (2026-09-15)
Version 0.8.2-feature-namespace-supplier-SNAPSHOT, built from feature/namespace-supplier at 7cf6e0b.
Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.
curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot-feature-namespace-supplier/mc-agent-admin-ui-app-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jarThe CLI is mc-agent-cli-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.
v0.8.1
Fixed
- agents: a file attached to a chat is findable on disk, not just by
search. The document tools said in their own description that an attached
file "has no path, use vector_search for it" — it has one since the chat
keeps its uploads in its own directory, and a model follows the tool's
description over the prompt, so it never opened the file. The prompt now
also names the path the tools take (uploads/<name>from the working
directory) beside the absolute one,globsays that it returns files and
never directories —*in a directory of sub-directories finds nothing —
and the bundledcoding-assistantgetsfile_list, which it was missing. - agents:
vector_searchno longer claims to be the only way to an
attached file. Its description said in capitals that attached files are not
on the filesystem, which outweighed both the prompt and the file tools; it
now says what search is good for — a passage in a long document, several
files at once, a knowledge store — and that a file the prompt names with a
path is better opened. The bundleddefault-chatsays the same. - agents: an image attached to a chat is a file like any other. It was
only recorded on the session and handed to the model with the next message,
sofile_listdid not show it and no tool could open it; it is now copied
into the chat's uploads directory as well, named in the prompt with its
path, and removed again when the attachment is removed. Its content is
still not indexed — an image is not text to search. - agents: attaching a file activates
file_readandfile_listfor the
chat, next tovector_search. An agent that finds its tools by searching
had the search tool in front of it and the file tools only behind a
tool_searchcall, so it searched an attachment it was told to open.
What's Changed
- An attached file is a file on disk, and the tools say so by @beisdog in #79
- An attached image is a file on disk too, and the tools that read it are switched on by @beisdog in #80
Full Changelog: v0.8.0...v0.8.1
v0.8.0
Added
-
agents: the REST API (
/api/**) and the OpenAI Responses API (/v1/**)
accept a bearer token once authentication is on: a Keycloak access token,
checked againstKC_ISSUER_URI(MC_JWT_AUDIENCEScan require an audience),
or a personal API token. A user creates API tokens on the new profile page
behind the avatar in the admin UI's header; the secret is shown once, only its
hash is stored, and a token can be revoked at any time. A request without an
accepted token gets401with a JSON body saying why. Before, the API only
answered to a browser session with a CSRF token — no program could call it on
a secured installation; now it takes bearer tokens only, the browser session
no longer counts there, and Swagger UI's Authorize takes a token for
Try it out. The agent server (mc-agent-api-app) is secured the
same way withMC_AUTH_ENABLED=true; it had no authentication at all. Users
are recorded on sign-in (<data>/<namespace>/system/users/or themc_user
table). New modules:mc-user-core,mc-user,mc-user-pg,
mc-agent-security. -
agents: MCP servers as registered tools. An operator registers a server
under Admin → MCP Servers — a local process, a Docker image or a remote HTTP
endpoint — tries it out with Test connection, and its tools appear in the
tool catalog as<prefix>_<tool>(the prefix is fixed once registered, so
agent definitions keep working). Values inenvandheadersare used as
entered; a${NAME}placeholder is refused until users can set their own
variables — the environment of the server process is never read.
processanddockertargets start only where
mindconnect.mcp.allow-process/mindconnect.mcp.allow-dockerallow it;
unset, both are on while sign-in is off and off once it is on, because
registering asks for a login, not an admin role. A started server inherits
onlyPATH,HOME, locale, proxy and container-daemon variables from the
admin UI. Failed MCP calls reportError: …, so the chat and a workflow's
tool step see them as failures.
Discovered tools are cached on disk, so a restart does not start every server
again. Registrations live in<data>/<namespace>/system/mcp-servers/, one
JSON file per server; a Gmail registration ships disabled. The modules sit in
agents/mcp. -
agents: tool settings in the admin UI: a tool can be switched off or given
a different description for this installation, without touching any agent
definition. What an agent definition says about a tool still wins for that
agent. Stored in<data>/<namespace>/system/tool-settings.json. -
workflow: run attributes.
WorkflowContextcarries values the host
hands one run (getAttribute/setAttribute), passed in with
WorkflowExecutorService.executeWorkflow(workflow, params, attributes)or
WorkflowRunService.runWithAttributes(...). Every step of the run sees them,
parallel for-each blocks and called workflows included; a resumed run starts
without them. -
agents:
ToolCallScope.rootSessionId, the chat at the top of a sub-agent
chain, andToolInvoker.call(tool, arguments, scope), which the tool-call
workflow step now calls with the scope found on its run. -
agents:
ToolRegistry.releaseSession(sessionId)and
MultiToolProvider.releaseSession(sessionId): whoever ends a session lets
the tools go of what they hold for it. The tool test bench calls it after
every test, so testing an MCP tool no longer leaves a connection — or a
container — behind per click. -
common:
mc-file-repo, the file counterpart ofmc-jdbcfor stores that
keep one JSON document per file.Documentsreads without locking and lets
one writer at a time change a file — across the whole JVM, so two store
instances on the same directory take turns too — withcreate,
createIfAbsentandupdate(key, change)for changes that must not
overwrite each other. Writing while already holding a write lock fails at
once instead of risking a deadlock, a lock that stays taken times out naming
its holder, and a second process on the same partition (namespace) of a data
directory is refused — processes serving different namespaces share the
directory as before. The file session store is the first to use it. -
common:
RecordLoginmc-file-repo— an ordered log of JSON records
per parent (the messages of a conversation), one file per record, with an
in-memory index of the file names:appendhands out the next key under
the directory's lock, pages read only their records, and a deleted tail's
keys are never handed out again. -
agents:
MessageRepository.update(conversation, id, change)changes a
stored message in one step. -
common:
DocumentTable.insert(writes a new row,falsewhen the key
exists) andDocumentTable.update(key, change), which reads the row
FOR UPDATEand writes the change in the same transaction. -
common:
VersionsandStaleVersionExceptioninmc-commonfor such
checks, andcompute(key, change)inDocumentsandDocumentTable: decide
on the stored document, or its absence, and write — under one lock. -
agents: a session has a working directory.
AgentSession.workingDir
is the directory the user is in — the CLI sets it to where it was
launched (local mode) and changes it with/cd(a relative path taken
from the session's current directory), the chat's composer
has a folder button that names it and opens a chooser,POST /api/sessionstakes
workingDirandPUT /api/sessions/{id}/working-dirchanges it, an
embedding callsAgentRuntime.openSession(agent, user, path). The
file-rooted tools (file_*,glob,bash, the document tools,
vector_ingest_file) take it as their base directory ahead of a tool's
baseDiroverride and the configured default; the system prompt names
it; a sub-agent called from the session inherits it.globsearches a
working directory from.and no longer requirespaththere. The
directory must exist and, on a server, lie under the new
mindconnect.tools.working-dir-root(default:mindconnect.tools.base-dir;
the CLI sets/). Sessions written before read as having none, so
nothing changes for them. -
agents: a session can reach additional directories beside its working
directory (AgentSession.additionalDirs):/add-dirand/dirsin the
CLI, Additional directories in the chat's chooser,additionalDirson
POST /api/sessionsandPUT /api/sessions/{id}/working-dir,
AgentRuntime.addDirectoryin an embedding. Relative paths keep meaning
the working directory; an absolute path into an additional directory is
allowed, anything outside the session's directories is refused with an
error naming them.FileRootsinmc-agent-tool-spiis the one sandbox
check the file, glob and document tools andvector_ingest_filenow share
(ToolCallScope.fileRoots), and each of those tools takes aFileRoots
beside itsPathconstructor. -
agents: the chat's folder button opens a directory chooser that works
like the operating system's: one path field, the folders inside to step
into, Use this folder takes what the field says. It browses the server's
tree undermindconnect.tools.working-dir-rootand nothing beyond it; the
additional directories are listed beneath with a Remove each and an
Add this folder.GET /api/directories?path=is the same listing over REST,
over the caller's own tree, andPUT /api/sessions/{id}/working-dirchanges
a session of theirs — like every session endpoint, someone else's answers 404. The
root may carry{user}(/srv/mindconnect/users/{user}): then every
user has a root of their own, created on first use, and picks from and
works in that tree only — the way to run this on a multi-user server. -
agents: every user has a directory on the server, and every session
one of its own.mindconnect.users.home(default<data.base-dir>/<namespace>/home/{user})
is where a user's directories live; a session opened without a working
directory works insessions/<id>under it, the files attached to a chat
are put in that directory'suploads/— the system prompt names each
file's path, sofile_read, the document tools andbashopen them —
and when the session moves to a project, or a file is attached while it
works in one, its own directory stays
reachable as an additional directory. The users' home is also the
default root a working directory must lie under
(mindconnect.tools.working-dir-root), so the chat's chooser opens in
the user's own tree. An embedding sets it with
AgentRuntimeBuilder.usersHome. Sessions written before keep working as
they did: none of them gets a directory after the fact. A user id with
characters other than letters, digits,.,-and_gets a short hash
on its directory name, so two such ids never share a home. -
agents:
AttachedFile.path— where the copy of an attached file lies
on disk, when there is one;ToolCallScope.runWithbinds a scope to the
running thread, and a workflow tool-call step resolves its tools in that
scope, so an ingestion workflow runs against the session's own files. -
agents: a bundled
coding-assistantagent besidedefault-chat: it
works in the project chosen with the chat's folder button —grepand
globto orient,file_readwith line numbers,file_editfor exact
changes,file_writefor new files,bash(with approval) for the build
and tests,todo_writefor the plan, and theexplorersub-agent for a
sweep of an unfamiliar codebase. Forty tool rounds per turn, auto-compact
memory. -
agents: two file tools for editing code:
file_editreplaces one
exact passage of a file (old_string→new_string; the passage must
be unique unlessreplace_all) and returns a unified ...
v0.7.0
Added
- agents: a container image for the admin UI, published to GHCR on every
push tomainasghcr.io/mindconnect-ai/mc-agent-admin-ui. It is a layered
Spring Boot image, so a new version is a ~3 MB application layer on top of an
unchanged dependency layer instead of a fresh 344 MB — which is what makes
redeploying onto a small VPS practical. Chromium ships in the image, so the
Playwright-backed browser tools work without downloading a browser at
runtime.deploy/holds a single-host Compose setup to run it behind Caddy,
which obtains and renews its TLS certificates itself, next to Postgres and
Keycloak.
Changed
-
agents: every id is a typed record —
AgentId,SessionId,
ConversationId,MessageId,ChatTurnId,ParticipantId,
ConversationSummaryId,TraceId,LlmConfigId,FileId,AgentToolId
and friends — instead of aUUIDor a bareString. Ports, services,
repositories, the tool SPI, the builder facade and the CLI client take these
types, so afindById(AgentId)refuses aSessionIdat compile time. Id
values are lower-case, file-name-safe strings: a readableweb-researcher
is as valid as a UUID. In JSON an id is still its plain string.
AgentToolno longer points back at its agent: theagentDefinitionId
field is gone,AgentTool.of(name, …)takes no agent id, and older agent
files that still carry the field load as before. -
agents: the namespace is no longer part of any domain object, id, port
or service, andAuthenticationInfocarries only the user and roles.
Persistence adapters are bound to one namespace when they are built, and a
process serves exactly one: the Spring starters readmindconnect.namespace
(defaultlocal;MC_NAMESPACEin the apps), the builder takes
namespace(...), and adapters take it as their last constructor argument.
Everything a namespace stores on disk sits in one directory,
<mindconnect.data.base-dir>/<namespace>/—data/local/by default, with
files,workflowsandvector-storesbeside the rest — so the properties
mindconnect.file-store.dir,mindconnect.workflow-admin.dirand
mindconnect.vector-store.dirare gone, and existing data has to move into
that directory (or start with an empty one).
On Postgres everymc_*table carries anamespacecolumn in its primary
key and the workflow tables apartition_key, and pgvector tables are named
vs_<namespace>__<store>; there is no migration, so drop themc_*tables
of an existing database before starting this version (existingvs_*
stores are not picked up either). The REST API loses itsnamespace
parameters — the query parameter of the agent endpoints and of
GET /api/sessions, and the field in the bodies ofPOST /api/agentsand
POST /api/sessions— agent, session and conversation JSON lose their
namespacefield,AgentDefinition.create(...)its namespace argument, and
the propertymindconnect.agent.stateless.namespaceis gone. -
agents: the protocol (
mc-agent-protocol) no longer names a tenant:
Sessions.open(agentName)andConversations.create()lose their
namespaceargument, andSessionandConversationtheirnamespace
field.AgentRuntimeBackendneeds none either — the runtime it wraps is
bound to one. -
agents:
NamespaceandAuthenticationInfomoved frommc-common
(packageai.mindconnect.common) into the new dependency-free module
mc-agent-domain, packageai.mindconnect.agent, together withUserId,
whichAuthenticationInfo.userId()now returns instead of aString.
Embedders repoint two imports and wrap the user id inUserId.of(...); the
agents modules pull the new module in transitively.
mc-commonis tenant-free again, which is what the workflow and taskqueue
areas expect from it; its unusedDomainEventrecord went with the move. -
agents: the runtime's packages moved one level down, from
ai.mindconnect.agent.{domain,port,service,memory,tools,adapter}to
ai.mindconnect.agent.runtime.{domain,port,service,memory,tools,adapter},
so thatai.mindconnect.agentitself is left to the shared vocabulary.
Unchanged: the tool SPIai.mindconnect.agent.tool, the tool modules
ai.mindconnect.agent.tools.*and the memory strategies
ai.mindconnect.agent.memory.strategy. In the same spirit the Postgres
file store moved fromai.mindconnect.filestore.pgto
ai.mindconnect.filestore.adapter.pg. Embedders repoint their imports;
logger names configured for the old packages (for example
ai.mindconnect.agent.service) need theruntimesegment too. -
agents: the bundled agents default to OpenAI instead of a local LM
Studio server.agent-default, the LLM config every shipped agent
references, is now an alias that delegates toopenai-default, so a fresh
install works as soon asOPENAI_API_KEYis set — which is what most
people have — instead of requiring a 120B model onlocalhost:1234.
url-readerfollows suit and no longer needs the localgemma-reader
model. Repointing the alias (inagent-default.jsonor the Admin UI)
moves every bundled agent to another provider at once;lm-studio-default
still carries the previous local settings. Existing installations are not
changed behind your back: a storedagent-defaultthat differs from the
seed shows up as a pending migration in the Admin UI, and the CLI logs the
difference and keeps what it has.
Fixed
-
agents: a store file of the
memoryvector-store backend loads whatever
its formatting. It is written one chunk per line, and it was read that way
too, so a file someone had pretty-printed to read it counted as unreadable —
and the chat's attachment list then showed no chunks without saying why. The
file is now read as a sequence of JSON objects, and an unreadable session
store is logged as a warning. -
agents: a tool call no longer fails now and then with "tool call was
interrupted before completion: tool task ended FAILED without a result".
With the file persistence, a session was saved by truncating and rewriting
session.json; a tool task reading the session at that moment found an
empty file and failed before the tool ran. Sessions are now written to a
temporary file and moved into place in one step. -
agents: a sub-agent's approval question is answered only through the
chat that shows its card. The open questions were keyed by the tool call's
id alone, which the model provider hands out and which is unique only within
one response: an answer sent through another session reached the waiting
tool all the same, and two chats that got the same call id shared one entry,
so one card never appeared.POST /api/sessions/{id}/approvals/{callId}now
answers 404 when that session has no such open question. -
agents: the admin UI's Logout button no longer ends with "The backend is
unreachable". The header's logout is a link, and the SPA router turned every
such click into a fetch — but logout answers with a redirect to Keycloak on
another host, which a fetch cannot follow. The session was destroyed all the
same, so it logged you out and showed an error while doing it. Logout is now
an ordinary page navigation. -
agents: a tool provider that cannot say whether it is ready no longer
takes the catalogue with it.isAvailable()became a per-lookup call when
provider binding moved off the startup path; a provider throwing there would
have failed every turn and every catalogue render, for every provider. It is
now caught and logged, and the bundle counts as unavailable. The
documentation for tool authors says whatbindandisAvailablemay expect
of their surroundings — both run more often, and in more places, than they
used to. -
agents: the Spring runtime starts the tool warm-up once its context is
refreshed rather than while the registry is being built. Binding asks the
environment for services, and that environment resolves them from the
application context: doing it from a warm-up thread mid-refresh meant
queueing behind the very startup that was waiting for the first round, so a
provider that should have been ready in microseconds could miss its window
for no reason.SpiToolRegistry.deferred(...)is the registry without the
warm-up for a host that starts in phases; embedders that build it directly
are unaffected.
Changed
- agents: resolving tools no longer holds up the start. Providers that
reach outside the process — the Gmail tools spawn an MCP server in a
container — used to bind on the startup thread, so a machine without a
container runtime paid the full initialization timeout before the
application served its first request; here that was 47 seconds against 2.
Every provider now binds on its own virtual thread, the first round gets a
short grace period so in-process providers are ready when the runtime is,
and anything slower joins the catalog the moment it is ready. A provider
that fails for a passing reason is tried again a few times over about a
minute, which means a container runtime started after the application no
longer needs a restart to be noticed.SpiToolRegistryisAutoCloseable;
closing it stops the warm-up.
Fixed
-
agents: an MCP server whose command cannot be run says so at once
instead of holding the application back. The Gmail tools spawn their server
through docker; on a machine without it — or with a docker link left behind
by an uninstalled Docker Desktop — startup used to stall for the full
initialization timeout and log a dropped reactive error before deciding the
provider was unavailable. The proxy now checks that the command resolves to
something executable before spawning, and names it when it does not. -
agents: a transcription job asked to wait no longer answers with a
server ...
v0.6.0
Added
-
agents: a bundled
openai-embeddingsLLM config — OpenAI's
text-embedding-3-small(override withOPENAI_EMBEDDING_MODEL) with
OPENAI_API_KEY, for vector stores and file ingestion when no local
embedding server is running. Choose it in a store template or via
mindconnect.vector-store.embedding-config; the localembeddingsconfig
stays the default. -
agents: transcription over the REST API, as a job.
POST /api/transcriptionstakes a recording as multipart, stores it, queues
the work on the task queue and answers202with the job id plus both URLs
to follow it: one to poll, one to stream.GET /api/transcriptions/{id}
reports the status and, once done, the transcript with the detected language
and the recording's length;?wait=holds the request until the job ends,
for a caller who wants the synchronous feel without a loop.
GET /api/transcriptions/{id}/eventsis Server-Sent Events from the job's
channel — queued, running, the transcript as it forms indeltaevents,
then completed with the whole text — opening with the state as it is now,
so a late arrival is told what it missed, and?afterSeq=resumes a dropped
connection without a gap. The queue retries a provider hiccup, andmodel
names an LLM config whose alias is followed. The recording stays in the file
store, so a transcript keeps the audio behind it: its id and URL come back
with the job, andDELETE /api/files/{id}disposes of it when the caller
says so. Whether the text arrives word by word depends on the model —
OpenAI's transcribe models stream it,whisper-1answers in one piece and
sends one delta — and the events are the same either way, so a client does
not branch on the model. -
agents: speech to text — a Whisper model as a normal LLM config, and a
microphone in the chat. A config's type can now beSPEECH_TO_TEXTnext to
CHATandEMBEDDING, with the same provider, key, encryption and admin
form as every other model.LlmTranscriptionis the port callers use: they
name a config and get a transcript, so an alias is followed exactly as it is
for a chat model — pointspeech-to-textat whichever config should serve
and swap it later without touching a caller. Behind it,
OpenAiTranscriptionGatewaycalls the OpenAI-compatible
/v1/audio/transcriptionsendpoint, so OpenAI, Groq and a local Whisper
server all work by pointing the base URL at them; language, prompt and
response format are provider parameters on the config, and the LM Studio
model picker says it serves no transcription models rather than offering
chat ones.In the chat, the composer's new microphone records in the browser and puts
the transcript into the input, after anything already typed — nothing is
sent without the person who spoke it pressing Send. Dictating while a turn
is still streaming brings the words back as a sticky toast, since there is
no input to fill at that moment. The admin UI tests a
speech config the same way: drop an audio file into the test dialog, or
press Record, speak and press Stop. Both need a microphone and a
secure context (localhostor HTTPS); where the browser refuses, uploading
a file still works.A new installation is seeded with a
speech-to-textconfig (whisper-1
behind${OPENAI_API_KEY}, overridable throughSPEECH_TO_TEXT_MODELand
SPEECH_TO_TEXT_BASE_URL); an existing one gets it on the next start, since
seeding checks each entry by id.
Changed
- agents:
AgentLoop.runtakes the tokens spent by earlier attempts as a
further argument, andStreamEventhas a newTurnUsagevariant. Code
embeddingmc-agent-runtime-corethat callsrundirectly, or that
switches exhaustively overStreamEvent, needs the extra argument and the
extra arm.StreamEvent.Doneis unchanged.
Fixed
-
agents: the Responses API reports what a turn cost.
usageon
/v1/responseswas always zero: the runtime summed the providers' token
counts per round but dropped them at the turn boundary, so every response
claimed nought input and nought output tokens. The counts now ride with the
turn — across a suspend on a tool call and back — and reach the response
object and theresponse.completedframe. They arrive with the new
StreamEvent.TurnUsage, sent whenever the loop hands back control, so a
turn that fails or is cancelled reports what it burned instead of zero,
and the extra model call that answers after the round cap is counted too.
The same numbers ride themc-agent-api-restsession stream as a
turn_usageframe; a client talking to an older server sees none, which
reads as untracked rather than as a measured zero. Nothing on that API
carried a turn's cost before — the/memoryendpoint'stotalTokens
sizes the next prompt's context window, which is not what a turn spent,
and the per-call traces that do record it are rendered only in the admin
UI. -
agents: the Responses API stream announces and closes its content
parts. It sentoutput_item.added, the text deltas andoutput_item.done,
but none ofcontent_part.added,output_text.doneorcontent_part.done
— so a client had nothing telling it that the deltas and the text in the
finished item are one content part, and one that assumed otherwise rendered
the answer twice. A tool call now also gets the
function_call_arguments.donea client may wait for before dispatching, an
item opensin_progressand empty rather than already complete, and
sequence_numbercounts the frames actually written, so it no longer skips
the numbers of protocol events this layer does not send. -
agents: a Responses API stream no longer closes a content part it
never opened. When text reaches the runtime finished rather than as tokens
— a reviewer's replacement answer — the item was closed without ever being
announced, and a client resolving those frames against
response.output[output_index]aborted the stream on the missing entry
(the official OpenAI SDK raisesIndexError). Such an item is now
announced before it closes. -
agents: a Responses API stream can no longer start out of order. A
subscriber was registered while the backlog it had yet to receive was
handed over outside the lock, so an event emitted in between overtook it: a
reader could see a later delta before an earlier one, and a terminal event
that won the race closed the stream on a backlog never written. The backlog
is now delivered under the same lock that registers the subscriber. -
agents: two messages appended at the same moment no longer claim the
same place in a conversation. The sequence number was read and written in
two steps, so a turn finishing several tool calls together could hand the
same number to two messages and skip the next, which throws off anything
cutting by it: delete-from-here, regenerate, and the tool cards a turn is
folded into. Handing out the number is now the store's own doing, in one
step: Postgres reserves it in a counter row, the file and in-memory stores
take a lock per conversation.MessageRepositorygainsappendand loses
countByConversationId, whose only caller this was. -
agents: a chat longer than 200 messages no longer hides its newest
ones. A session's history was loaded as the first 200 messages, so once
a conversation passed that the chat kept showing its opening and silently
dropped everything after it, the message just sent included. It looked
like the message disappearing and the view jumping back a turn, while the
agent went on answering it, because the runtime had always read the
conversation entire. The history now comes back entire too, in the chat,
the admin UI's chat, the attached-files list and regenerate. -
agents: editing the built-in
defaultvector-store template in the Admin
UI no longer pretends to save. The built-in template is the
mindconnect.vector-store.*properties; its edit form now says so and saves
the values as a copy under a name of your own, and saving or deleting it
under its own name reports why nothing happened instead of staying silent.
What's Changed
- A long chat shows its newest messages, and two appends get two numbers by @beisdog in #50
- An OpenAI embedding config, and a template form that admits what it cannot save by @beisdog in #51
- Fix token streaming and accounting on the Responses API by @beisdog in #52
- Speech to text: a Whisper config, and a microphone in the chat by @beisdog in #53
Full Changelog: v0.5.2...v0.6.0
Snapshot fix/tool-warmup-review · 0.6.1-fix-tool-warmup-review-SNAPSHOT (2026-09-10)
Version 0.6.1-fix-tool-warmup-review-SNAPSHOT, built from fix/tool-warmup-review at 67594cc.
Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.
curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot-fix-tool-warmup-review/mc-agent-admin-ui-app-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jarThe CLI is mc-agent-cli-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.