feat(sicore-a2a-mcp): support multiple named A2A keys selected by alias#443
Open
LikiosSedo wants to merge 1 commit into
Open
feat(sicore-a2a-mcp): support multiple named A2A keys selected by alias#443LikiosSedo wants to merge 1 commit into
LikiosSedo wants to merge 1 commit into
Conversation
One adapter process could previously bind only a single A2A key, so
switching Siclaw agents meant editing config and restarting. Passing the
key as a tool argument is not an option: credentials must never flow
through the model context.
Add configuration-side named keys, selected at call time by alias:
- New SICLAW_A2A_KEYS env: JSON {alias: key}. Aliases match
^[a-z0-9][a-z0-9_-]{0,31}$. The single-key SICLAW_A2A_KEY /
SICLAW_A2A_KEY_FILE form is unchanged and maps to the reserved "default"
alias; the two forms may be combined. SICLAW_AGENT_ID still pins the
default key and is rejected alongside SICLAW_A2A_KEYS. Every key is
self-resolved once at startup and any failure aborts boot (fail-fast).
- Every tool gains an optional "agent" alias argument (never a key). Tool
descriptions are populated at startup with the configured aliases and
their resolved agent ids. With one agent "agent" is optional; with
several, a create/list call that omits it errors with the alias list
rather than guessing.
- task_id -> alias is tracked in process for the server lifetime, so
wait/get/cancel auto-route to the creating key. A mismatched "agent"
argument is overridden by the recorded creator and noted in the result.
siclaw_list_tasks without "agent" aggregates across every key, tags each
task with its alias, and rebuilds the map after a restart.
Keys stay in configuration only: never a tool parameter, never logged,
never present in an error message (errors carry aliases and agent ids).
Route the new logic through an AgentRouter; keep SicoreA2aClient per-key.
Existing tests updated to the router API; add router unit tests, multi-key
routing tool tests, and a two-alias stdio e2e cold-start smoke.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
One
sicore-a2a-mcpadapter process binds a single A2A key, i.e. a single Siclaw agent. Switching agents means editing config and restarting. The obvious shortcut — passing the key as a tool argument — is exactly the thing we must not do: A2A keys are credentials and must never flow through the model context.Approach
Put multiple named keys in configuration, and let the model select an agent by a short alias passed as an optional
agenttool argument. The alias is the only agent selector that crosses the model boundary; keys stay in config/env only.Changes
SICLAW_A2A_KEYSenv, a JSON object{"alias":"sk-..."}. Aliases must match^[a-z0-9][a-z0-9_-]{0,31}$.SICLAW_A2A_KEY/SICLAW_A2A_KEY_FILEkeep working and map to the reserved aliasdefault. The two forms may be combined; adefaultalias insideSICLAW_A2A_KEYSis a collision error.SICLAW_AGENT_IDstill pins thedefaultsingle key; combining it withSICLAW_A2A_KEYSis rejected (each named key resolves its own agent).GET /api/v1/a2a/self; any failure aborts boot with the failing alias named (fail-fast, no silent partial key).agentalias argument (never a key). Descriptions are populated at startup with the configured aliases and their resolved agent ids. One agent →agentoptional; several → omitting it on a create/list call errors with the alias list instead of guessing.task_id/context_idare per-key server resources. The adapter recordstask_id -> aliasin process memory, sosiclaw_wait_task/siclaw_get_task/siclaw_cancel_taskauto-route to the creating key. A mismatchedagentargument is overridden by the recorded creator and arouting_noterecords the override.siclaw_list_taskswithoutagentaggregates across every key, tags each row with its alias, and rebuilds the map after a restart.Implementation routes the new logic through a small
AgentRouter;SicoreA2aClientstays per-key and unchanged.Scope
The remote Sicore MCP endpoint (
/api/v1/mcp) is out of scope: each remote client config carries its ownAuthorizationheader, so multi-agent there is just multiple client configs. This alias multiplexing is only for the local stdio adapter that injects credentials from env/files. The README documents this split.Tests
npm test— 56 passing (was ~30).defaultmapping, merge, alias validation, collision,SICLAW_AGENT_ID+SICLAW_A2A_KEYSrejection, non-string/malformed key values not echoed.agent, unknown alias, named routing, auto-route follow-ups, override note, aggregated list, ownership recovery./selfresolution, description injection, alias routing, and no key leakage in descriptions or the stderr ready line.Manual fail-fast smoke confirmed for missing key / bad alias / collision /
SICLAW_AGENT_ID+SICLAW_A2A_KEYS— each exits 1 with a clear message and no key echoed.Not published: no npm publish, no tag — release is left to the maintainer.