Repository navigation
Releases: mhidalgo-lab/agility-bridge
Releases · mhidalgo-lab/agility-bridge
Release list
v0.8.0
Minor Changes
- 2974387: Client integrations (P8-06):
agility-bridge client-config <client>prints the setup snippet (and the VS Code or Cursor install link) for VS Code, Claude Code, Claude Desktop, Cursor, Copilot CLI and Windsurf, never holding a token. The repository is now a Claude Code plugin marketplace with anagility-bridgeplugin (MCP entry with sensitive settings, four read-only commands, two skills), kept in version step with the package. - 1eb5c57: Configuration export and import (P8-05):
agility-bridge config exportwrites instances, shared credentials, workspaces and service grants to a file with no secrets (tokens only with--with-tokens, sealed with a passphrase: PBKDF2-HMAC-SHA-256, 600,000 iterations, AES-256-GCM);config importis additive and a dry run unless--apply, goes through the admin API, never changes what exists and never assigns or reads a personal credential. - 6fbba59: Deep links (P8-02): every asset row in
structuredContentcarries aurlthat opens the item in the Agility UI (assetdetail.v1), built only from the pinned instance URL and a validated OID or number; the text carries one fixed note. No share or public links are ever created. - c4f173a:
agility-bridge doctor(P8-04): a read-only diagnosis of the configuration, the instance (an unauthenticated probe over HTTPS), the token (only with--check-token), the local database and master key, and the MCP client files of every primary client. Output is scrubbed;--fixapplies only the owner-only file modes it lists. - 6aeaae3: Per-grant Agility request quotas (P8-09):
AB_QUOTA_DAILYandAB_QUOTA_MONTHLYcap the requests one grant may cause per UTC day and month (off unless set). A read over its quota is refused withAB_LIMIT_QUOTAbefore anything is asked of Agility; retries and query-cache hits are not counted; a counter outage fails open. Usage shows inwhoami, the newGET /grants/{id}/usageadmin route and the grant page of the panel, with a warning at 80%. See docs/guide/query-limits.md. - 3c50fd2: A keychain token is bound to the instance it was stored for (P8-31):
agility-bridge keychain set <name> --for-url <address>records the host next to the token, and the server refuses to sendkeychain:<name>to a differentAB_AGILITY_URL(a project's client file can no longer redirect your own token).agility-bridge keychain bind <name> --for-url <address>binds an existing entry or confirms a new address; entries stored without a host still work with a warning on stderr and indoctor.keychain status --nameshowsboundTo,keychain deleteremoves both entries. - dba024e: MCP Apps views (P8-03):
get_sprintlinks a sprint burndown chart andsprint_healtha status distribution chart (ui://agility-bridge/…) for clients that declared the MCP Apps extension; every other client gets the same text and structured result as before. The views are static, dependency-free pages with a strict content security policy and show Agility text only as text. On by default;AB_MCP_APPS=offturns them off.sprint_healthalso returnsbyStatusCategory. - 3931f1c: MCP Tasks extension (P8-10): a 2026-07-28 client that declares
io.modelcontextprotocol/taskson atools/callof a report tool gets a task handle at once;tasks/getruns the report inside that request,tasks/cancelandtasks/updatecomplete the lifecycle. Records are sealed, hashed, bound to the owner device, grant and workspace, and expire after 15 minutes; nothing runs after a response.AB_TASKS=offremoves the extension. See docs/guide/tasks.md. - 4ffbf70: OS keychain support (P8-08):
AB_AGILITY_TOKEN,AB_MASTER_KEYandAB_MASTER_KEY_PREVIOUS(and the YAML${keychain:…}form) acceptkeychain:<name>references to macOS Keychain, Linuxsecret-tooland Windows Credential Manager entries. Newagility-bridge keychain set|delete|statusandinit --keychain. Secrets travel on standard input, never in arguments; a missing tool or entry is an error, with no fallback. The Windows backend is unverified on a real machine. - 2448c53: Public tool catalogue (P8-11):
AB_PUBLIC_TOOL_CATALOG=on(off by default) letsinitialize,ping,server/discoverandtools/listanswer without a credential, from a static read-only catalogue that is the same for every workspace name and never lists write, report or attachment tools. Everything else still needs authentication. - fb6e06d: Query cache (P8-01, off by default):
AB_QUERY_CACHE_TTL(0 to 60 seconds) answers repeatedquery.v1reads from a bounded in-memory cache on stdio,serveand the Worker, with entries named by a keyed hash over the workspace, grant, credential, projects, asset types, limits and the compiled query. Callers with write tools never use it. On Workers,AB_QUERY_CACHE_EDGE=onadds a sealed Cache API tier that reports whether a read-back proved it works.whoamishows the cache status. See docs/guide/query-cache.md. - ae19fbd: Self-monitoring with OpenTelemetry (P8-07):
AB_OTEL_ENDPOINT(and optionallyAB_OTEL_HEADERS) makesserveand the Worker send one span per MCP request to your OTLP/HTTP collector, off by default. Spans carry only an allowlist of attributes (MCP method, tool name, status, error code, fault class, duration, Agility request count, request id), never Agility content, credentials, people, workspaces or query values. Caller faults are never counted as service errors.traceparentis read strictly, only from authenticated callers, and never forwarded to Agility. See the observability guide for the span reference and dashboard and alert recipes.
Patch Changes
- 702dbb4: Self-monitoring on Workers (P8-43): spans are queued per isolate and sent in batches (64 spans, about 128 KiB or five seconds) through the
waitUntilof the request that triggers the send, instead of one collector request per request; the queue is bounded in count, bytes and sends in flight, and spans of an evicted isolate are lost (at most five seconds). Fixes the OTLP exporter on Workers, which workerd rejected (redirect: 'error'), so no span was delivered from a Worker before. - 9951518: A
${keychain:...}in theinstance.tokenof the YAML configuration file is now sent only to the host it was stored for, likeAB_AGILITY_TOKEN=keychain:<name>(P8-42): a differentinstance.urlstops startup before the secret is read and nameskeychain bind. Entries stored without a host still work with a warning. - 09e350a: Cheaper repeated work (P8-35): the public tool catalogue's
initialize,server/discoverandtools/listanswers are kept per process or Worker isolate with only the JSON-RPC id replaced, and identical reads in flight together inside one request send one Agility request when the query cache is on. Expired query-cache entries are dropped wherever they sit in the map. - d1019f8: Deep links (P8-40): the instance URL is parsed once per result instead of twice per row, and the links of one result add at most 24,000 characters; rows past that get no
urland the note says how many. - be2c84e: Phase 8 review fixes (P8-12) for the remote hosts: the shared tier of the query cache never serves an answer older than one TTL, and a read-only grant in a workspace where others write uses the cache; a refused quota no longer spends a call-budget unit; on Workers the quota counters and the cache's shared-tier store finish after the response; MCP Tasks validate the call and cap creations per grant at creation, return a finished result only while the grant still has the tool and scope, report their real remaining life, fail (not rerun) a report that does not fit one request or is too large to keep, and honour a cancel that races the result; self-monitoring samples spans of unauthenticated requests (30 a minute) and the public catalogue no longer advertises the Tasks extension.
- eff6579: Phase 8 review fixes (P8-12): config files that carry tokens now authenticate the instance addresses and the credential mapping (file version 2: export again);
config import --add-grantsfinishes a partly failed import; environmentkeychain:references read theagility-bridgeservice only;keychain statusprobes the store;doctor --offlinereads no keychain,doctor --fixleaves an explicitAB_DB_PATHdirectory alone and control characters are stripped from its output;client-config claude-codeprints a quoted command; the stdio server warns about settings only remote hosts read; the Windows keychain script pins UTF-8. - dadfd4a: Quota (P8-39): metadata requests (
meta.v1) are no longer counted againstAB_QUOTA_DAILYandAB_QUOTA_MONTHLY(they are still refused once a period is used up), and a request now adds to the day and the month counter in one storage statement (two D1 statements a request instead of three). - ddb6ab8: Shared tier of the query cache (P8-38): entries above 64 KiB stay in the isolate's memory instead of being sealed into the Cache API (an option of the tier, shown by
whoamiasqueryCache.sharedMaxEntryChars), and sealing uses the runtime's own base64 where it exists (about 100 times faster on workerd).
v0.7.0
Minor Changes
- f04771d: Approval policy (P7-26): an owner can require the approval page for every write, per workspace (
writePolicy.approval: page_only) or per grant (rateLimit.writeApproval: page_only). Under it a client that can elicit is never asked, so a client that accepts every question cannot approve. The default stays as it was. - 26203bd: Out-of-band approval page (P7-02): a write that the client cannot ask the person about now answers
AB_WRITE_APPROVAL_PENDINGwith a link to/admin/approve/<id>in the signed-in panel, where the person who made the request reviews the exact change and approves or declines it; the agent then repeats the call. Single use, bound to the change and the workspace, short expiry, CSRF and a recent sign-in to approve. Only withAB_WRITE_TOOLS=onand sign-in configured; no write tool ships yet. - 1377f45: Approval request limits (P7-27): a connection can ask for at most 30 approvals an hour (
AB_LIMIT_RATE, scopeapproval_requests; nobody is asked past it), and a person with 20 approvals waiting on the page gets a refusal for the next one instead of losing the oldest. - 21b26b6: Relations on create and update (P7-21):
create_work_itemtakesowners(at most three members),teamandsprint, andupdate_fieldstakesparent(re-parent under an epic, or a task or test under a story or defect of its own project) andproject(move a story, defect or epic to another open project of the workspace; an item in a sprint is not moved). Each is found inside the workspace's projects, shown old to new in the approval and re-checked before the write. The declared costs of both tools grow with the new paths. The request shapes are unverified until the sandbox run. - 8e5c316: Show the OAuth write scope on My access and on the administrators' Connected apps list (P7-32): each connected app is marked "can ask for changes" (its authorization holds
mcp:write) or "read only", andcanWriteis inGET /api/admin/v1/me/oauth-devicesandGET /api/admin/v1/oauth/devices. No scope string is shown, and the way to remove it is still to disconnect the app. - 4051a0b: OAuth
mcp:writescope (P7-24): an app that signs in with OAuth can ask formcp:writenext tomcp:read; the consent screen then shows a separate, never-ticked, never-remembered "allow changes" box, and the scope is granted only when the server switch, the workspace, the grant and a personal credential allow writes. The token's scope reaches the access layer, so a token without it never lists or runs a write tool; a refresh keeps the scope and cannot add it.mcp:writeis advertised only withAB_WRITE_TOOLS=on. - 168d94d: A list of my pending approvals (P7-14):
/admin/approvein the panel (linked from My access) lists the signed-in person's own open write requests with the tool, item, workspace, field names and expiry, and a link to each request's page. It shows no values and never another person's request. - d287c8f: A per-grant write limit (P7-15): an owner (with a recent sign-in) can give a grant its own writes per hour, in the panel's grant form or as
rateLimit.writesPerHourin the admin API. The write limiter uses the lower of it andAB_WRITE_RATE_PER_GRANT_HOURLY, a value above the server's is refused, and the change is audited with the previous value. - 007216a: Wave-1 write extensions (P7-17), as arguments of existing tools:
move_to_sprinttakesto_backlog: trueto take an item out of its sprint;log_efforttakesmemberto log for another member who already owns the item (otherwise refused before any approval; the approval names the member);update_fieldstakestodofor the remaining hours of a task or test. Each shows old and new in the approval. The request shapes are unverified until the sandbox run. - c17b7da: Workspace-enabled writable custom attributes (P7-20): an owner lists the
Custom_attributes an agent may write per workspace (panel,PATCH /workspaces/{id}withwritePolicy.customFields, orAB_WRITE_CUSTOM_FIELDSfor stdio), andcreate_work_itemandupdate_fieldsaccept them in acustomargument as text, long text or a number, checked against the instance's own attribute. Anything not on the list is refused before any request; the approval shows each value old to new; no tool description names an instance attribute. The request shape is unverified until the sandbox run. - a823608: Write approvals (P7-28): an approval is now used up just before the change is sent, not when it is found, so a limiter refusal, a conflict or an audit outage no longer makes the person approve again. It stays single use: two concurrent calls on one approval send once.
- 8b4e735: Write rows in the audit viewer, CSV and statistics (P7-11): the panel pairs each write's intent with its outcome and shows the tool, target, field names, approval channel and change hash; an intent with no outcome after ten minutes is flagged. The CSV gains
write_*columns, andGET /api/admin/v1/audit/write-stats(owner and admin) with a Writes section on the Usage page counts writes by tool, approval channel and outcome. A member still sees only their own write rows. - 40404f9: Show the remaining write budget (P7-16):
whoamiand every write tool's dry run report the caller's own grant's writes used, left and the end of the hour (and that the deployment is spent), and an owner or administrator sees the server-wide use on the panel overview and a grant's use on its page (GET /api/admin/v1/writes/budget,GET /api/admin/v1/grants/{id}/write-budget). Reading takes no unit and a store that cannot be read shows "unknown", never "unlimited". - c2619f5: Write framework (P7-01): the pipeline every future write tool will use (enablement layers, dry run,
confirm, approval by elicitation, single-use approval ledger, re-read and conflict check, idempotent no-op, durable intent audit before the change, one send, outcome audit).AB_WRITE_TOOLSandAB_WRITE_APPROVALare now parsed; no write tool ships yet, so nothing changes for a running server. - ed4da13: Write limiter (P7-03): every write takes one unit from a per-grant and a deployment-wide hourly counter (defaults 20 and 200,
AB_WRITE_RATE_PER_GRANT_HOURLY,AB_WRITE_RATE_DEPLOYMENT_HOURLY). The counters live in the shared storage, so they hold across Workers isolates and replicas, and the limiter fails closed (AB_WRITE_LIMIT_UNAVAILABLE). A refused write makes no upstream call. No write tool ships yet. - 35081a9: Lifecycle write tools (P7-06):
close_assetandreopen_asset,destructiveand separately enabled in the opt-inwritesgroup. Each acts on one work item, sends the type's own Agility operation (chosen from its metadata) once after approval, refuses a close while the item has open children, and treats an already-closed or already-open item as a no-op. The operation names are verified against a sandbox in P7-07. - a9c410b: Write tools, wave 1 (P7-04):
add_comment,update_status,log_effort,assign_ownersandmove_to_sprint, in the opt-inwritesgroup. Each changes one work item, shows the exact change (old to new) for approval, re-reads the target before sending and sends once; they are absent unlessAB_WRITE_TOOLS=onand an owner enables them per workspace and grant. The request shapes are the documented ones and are verified against a sandbox in P7-07. - 4cc7a4b: Write tools, wave 2 (P7-05):
create_work_item(a story, defect, task, test or epic in a project of the workspace; a task or test under its parent) andupdate_fields(title, description, estimate and priority of one open item). Typed fields per asset type only: no custom attributes, project, status, owner, sprint or parent changes. Each shows the exact change for approval (every field of a new item, old to new for an update), a repeated create is refused instead of creating twice, and both are absent unlessAB_WRITE_TOOLS=onand an owner enables them. The request shapes are verified against a sandbox in P7-07.
Patch Changes
- 69fcb1f: Write approvals (P7-29): a value that contains the words "untrusted data" is shown exactly as it will be sent (the first letter is written as a JSON escape) instead of being rewritten to "untrusted~data".
- 222f1f7: Workers Free plan (P7-30): the deployment-wide write cap defaults to 60 an hour there (200 elsewhere) and a larger
AB_WRITE_RATE_DEPLOYMENT_HOURLYstops the Worker at start, so write traffic alone cannot exceed the plan's D1 row budget. The grant and denial of an approval are read with one statement. - 8cee179: Writes cost fewer storage round trips: the approval grant and denial reads run only where the approval page exists, and
kv.listandkv.deletePrefixon SQL storage (D1 and SQLite) read a key range instead of scanning the table.close_assetcounts open children in one request andcreate_work_itemlooks up the caller once. The write tools' declared costs now follow their measured worst case (assign_owners18,reopen_asset10,create_work_item20,update_status15,add_comment18). - 4a69552: Write path review fixes (P7-09): a timeout or unavailable error from a write now always says the change may or may not have been applied, and a 2xx answer with an empty or unreadable body is an applied write;
close_assetrefuses when it cannot count every child; text from Agility is cleaned for display and no longer blocks a write; zero-width joiners between visible characters are allowed in your text; the comment and effort no-ops apply only within 10 minutes; the approval page cleans and caps the title; 2026-07-28 approval rounds are bound to the workspace and instance; overwriting tools advertisedestructiveHint; the write limiter is required and a refused check spends nothing; the create duplicate check handles unreadable times;update_fieldsrefuses to overwrite a rich description.
v0.6.0
Minor Changes
- 86b116a:
get_attachmentreads long text in steps:offset_charactersandnextOffset(each step downloads the attachment again). - 3b30c41: Collaboration tools:
get_conversations(comments of an asset, newest first, paged),list_attachments(attachment
metadata and external links as text) and the opt-inget_attachment(text of one attachment, off by default in its own
attachmentsgroup). The download is enabled per workspace (attachmentDownload,attachmentMaxByteslimits; a grant
can only turn it off or lower it) or withAB_ATTACHMENT_DOWNLOAD,AB_ATTACHMENT_MAX_BYTESandAB_ATTACHMENT_TYPESin
stdio; it checks scope through the parent asset, the content type on the metadata and the response, counts the size on
the stream, and is audited. - 3d4f694: Composite reports in a new opt-in
reportsgroup:sprint_health,standup_summary,release_readiness,defect_triage
andwork_item_quality(scored with the versioned rubric). Each report reads through its sibling tools with the call's own
scope and budget, skips and names any section whose tool the caller was not granted, and never turns missing data into a
zero. The group is off by default: enable it per workspace (limitreports; a grant can only turn it off) or with
AB_REPORTS=onin stdio.search_work_itemsnow listschangedAtwhen sorted by recent change. - 8ffe617:
defect_triageshows each defect's priority and severity where the instance has them and orders the flagged list by the instance's own priority rank. - 8fa45c3: New tool
get_sprint(planning group): one sprint with its UTC dates and schedule, backlog items and defects by status category, committed (at the begin date) vs done, scope change, carry-over of a finished sprint and a daily burndown read from bounded history. Sprint names and OIDs now also resolve closed (finished) sprints, and a status without a rollup category is no longer counted as "not started". - 35065d4: New tool
get_velocity(planning group): the completed estimate of the last past sprints of a team or Agility project,
each read as it ended, with the average over the sprints read in full; sprints that cannot be read are named, never
estimated. Capacity is not reported. - e991057: New tools in the
historygroup:get_asset_history(what changed on one asset in a bounded window, from the authoritative Agility history, one row per changed field with who and when) andlist_recent_changes(work items changed in a bounded window in a project, a team or the workspace, newest change first). Both validate the window and the row limit before any request; the activity stream is not used. - 6447ef0: MCP prompts:
review_backlog_item_quality,sprint_review,standup,triage_defects,draft_release_notes,
refine_backlogandexplain_asset, registered from one manifest and offered (prompts/list,prompts/get) only when
every tool they use is allowed, on stdio and HTTP. A prompt is static text that tells the model which tools to call, to treat
what they return as untrusted data and to stay read-only; its string arguments are validated and never reach a query. The
three that use report tools need the opt-inreportsgroup. - 4d5ff63: MCP resources: the server now offers reference material an agent can read on demand: the structured filter vocabulary, the instance's asset types (and one resource per readable type), the allowed Agility projects as a tree, and the versioned backlog item quality rubric. Resources are served through the tool handlers with the same limits, cache, scope rules, untrusted-data envelope and audit as the tools, and a resource is offered only when the tools it is served through are allowed for the workspace and grant.
- ba6301b: OAuth:
AB_OAUTH_CIMD_HOSTSlimits metadata-document clients to a list of host names; any other client id is refused before
anything is fetched. - 947ef44: OAuth: My access shows when each connected app was last used, and
AB_OAUTH_IDLE_TTL(off by default) ends an app that
nobody has used for that long at its next refresh. - 930983f: Issues, requests, goals, themes and retrospectives are first-class through the generic tools: four recipe resources (issues, requests, goals and themes, retrospectives) with example
query_assetsandget_assetsinputs, offered when those tools are allowed, and a default field tier that includes the owner of issues (which have a single owner). No new tools. - 59df125: Add the
portfoliotool group:list_portfolio_items(epics with category, status, parent, estimates and child counts, filtered and paged) andget_portfolio_tree(an epic with its descendant epics and roll-ups of items, done items, open defects, estimate and done percentage; a depth, size, budget or permission cut is reported aspartialand marks the roll-ups it touchesincomplete). The stdio server serves the group by default. - 878a99c:
get_portfolio_treecontinues a very large epic with acursor: the totals are carried between steps and the last step is exact. - 7f80d5f: Prompt argument completion:
completion/completesuggests teams, projects and sprints (andcurrent,previous,next) for the prompts that are offered, through the list tools the caller is granted. - dcc0870: Quality recipes: two new MCP resources (
agility-bridge://recipes/tests-and-defectsandagility-bridge://recipes/regression-assets) with workedquery_assetscalls for tests, defects, regression tests and test sets. They are offered only when the generic query and metadata tools are allowed. No new tool. - d581ab3: New report
quality_scan(opt-inreportsgroup): scores the open stories and defects of a sprint or an epic with the same versioned rubric aswork_item_quality, weakest first, from one batched read; more than 50 items per type are reported as partial. - 32ca06a:
list_recent_changesnow includes epics (portfolio items):asset_typeacceptsEpic, and with no type given epics are listed with the work items, newest change first, under the same bounded window, scope and team filters. - 3e10700:
agility-bridge user unbind --to <provider> [--ttl-minutes N](andprovider,ttlMinutesin the admin API and a field in
the panel) narrow who can claim an unbound account: only a verified sign-in at the named provider can bind it while the
claim lasts (default 24 hours, at most 7 days).
Patch Changes
- 1c9b48e:
whoaminames the granted tools the per-call budget hides (with their declared cost); the admin panel shows the same note on a workspace or grant with an explicitcallBudget. - 2e8c8ef:
get_sprint: a burndown history request that exceeds the response limit now leaves out its points (named innotes,
partial) instead of failing the whole call; large page sizes send fewer queries per request. - 636cd4a: Consistency pass over the 26 tools (P6-12). Shared arguments (
asset,project,team,sprint,member,time_range,status,limit,cursor,format,include_closed,minimal_output) now have one description each, from one schema, and every tool uses it; titles, the paging and cost sentences and descriptions open the same way everywhere ("Cold call: up to N Agility requests").list_membersnow refuses a cursor that is not its own even when the workspace's projects list no members.list_my_workandlist_recent_changesrows start with the identity columns like every other list (number,assetType,oid,state,project, then the tool's own columns).get_sprint(not yet released) names its countsitemCountinstead ofitems(totals,byStatusCategory[],committed,planned,carryOver, burndown points), andsprint_healthreads them under the new name.get_assets,list_asset_types,get_team,get_work_itemandwhoaminow declare thepartialandnoteskeys they already returned in their output schemas. The advertised output schemas no longer repeatadditionalProperties: {}: the defaulttools/listshrank from 39,179 to 35,892 bytes and the every-group one from 47,946 to 44,157 bytes, and the tool descriptions were reworded so the lexical router eval improved (65.8 % to 75 % top-1). No argument or released output field was renamed, so there is no deprecation alias. - 0ab45f7:
whoamiand the admin panel (workspace and grant pages) now warn when a composite report is granted without the sibling
tools it reads. The admin API's tool catalogue (GET /tools) lists each composite'ssiblings. The cost of every tool is
now tested against its declared worst case in one test over the whole catalogue. - ed94256: A malformed cursor is refused as
AB_INPUT_INVALIDbefore any name is resolved or request made, in every paged tool. - b2cbccb:
get_work_itemrenames itsminimal_outputargument toskip_related(it leaves out children, links and comments), sominimal_outputmeans one thing everywhere (identity columns only). The old name is deprecated: it still works onget_work_itemfor one release (mapped toskip_related, with a deprecation note in the result, not advertised intools/list) and will be removed in 0.7.0. Small internal helpers were deduplicated. - 696a104:
defect_triagelists the most similar duplicate pairs first, says when pairs or flagged defects were cut and ignores common English words in names. - f58df3d: The admin panel's workspace and grant forms have a "Check these tools" step that shows which composite reports would be granted without the tools they read, before saving.
- dfc47d2: Tool evaluation for this release (Claude Sonnet 5.5 and Opus 5.5, through the maintainer's router): no injected instruction
followed in 70 trials; tool choice 86.3 % and 90.4 % allowing one lookup. Reports:docs/dev/eval-reports/2026-10-28-sonnet-5-5.md
anddocs/dev/eval-reports/2026-10-28-opus-5-5.md, with the caveats indocs/dev/evals.md. - 75ed44c: The Worker keeps parsed asset-ty...
v0.5.0
Minor Changes
- 1fc3c53: OAuth abuse controls (P5-09): the authorization, consent and token endpoints, dynamic registration and metadata-document fetches now have shared, exact limits per address and per deployment, kept in the database (SQLite on Node, D1 on Workers) with atomic counters; failed token exchanges are limited per address (and, for addresses that already failed, in all). Refusals answer
429withRetry-Afterand are audited once per window (oauth.request.refused). Unused registrations are swept on a schedule (Node hourly, the Worker's cron in bounded slices), andservenow also purges expired rows hourly. New settingsAB_OAUTH_AUTHORIZE_IP_LIMIT,AB_OAUTH_AUTHORIZE_LIMIT,AB_OAUTH_CONSENT_IP_LIMIT,AB_OAUTH_CONSENT_LIMIT,AB_OAUTH_TOKEN_FAILURE_IP_LIMIT,AB_OAUTH_TOKEN_FAILURE_LIMIT,AB_OAUTH_DCR_LIMITandAB_OAUTH_CIMD_LIMIT. - 928072a: Add the administrators' view of OAuth (P5-08): with
AB_OAUTH=on, owners and admins see every person's connected apps (panel page "Connected apps",GET /oauth/devices) and disconnect one (POST /oauth/devices/{id}/revoke; an owner anyone's, an admin a member's), and owners list the registered OAuth clients (dynamic registrations and metadata documents, with where they send people and how many devices use them) and revoke one (GET /oauth/clients,POST /oauth/clients/revoke,POST /oauth/clients/restore). Revoking a client blocks it first, deletes its registration and ends every device of it: nothing new is authorized, no token is refreshed and its access tokens stop. Every change is a POST with the panel's CSRF checks and writes audit rows without token material. - bd692ff: OAuth client registration (P5-03): Client ID Metadata Documents (clients named by an https URL; fetched through a public-only, redirect-free, size- and time-bounded fetch, cached 5 minutes to 24 hours) and hardened dynamic registration (only the fields we use are kept, strict redirect rules, per-address and per-deployment caps, unused registrations expire after a day). New settings
AB_OAUTH_CIMD,AB_OAUTH_CIMD_MAX_CLIENTS,AB_OAUTH_CIMD_FETCH_LIMIT,AB_OAUTH_DCR,AB_OAUTH_DCR_MAX_CLIENTS,AB_OAUTH_DCR_IP_LIMIT,AB_OAUTH_DCR_REDIRECT_HOSTSandAB_OAUTH_DCR_UNUSED_TTL. The Worker sets theglobal_fetch_strictly_publiccompatibility flag. Still unreachable until the consent screen arrives. - d01a629: Add the OAuth sign-in and consent screen and connected apps (P5-04):
AB_OAUTH=onnow starts onserveand the Worker when a sign-in provider is configured./oauth/authorizesends a signed-out browser through sign-in and back, then shows a consent page (the app's unverified name, where it sends you, the workspace and the access it will get; strict policy, no inline script) whose approval is a CSRF-checked post with a single-use handle bound to the exact request, so replayed or swapped forms approve nothing;prompt=noneredirects errors only. Approved apps are listed on My access with Disconnect (and Disconnect all), at most 25 per person; an authorization request alone never ends an existing connection. New admin API routesGET /me/oauth-devicesandPOST /me/oauth-devices/{id}/revokeand/me/oauth-devices/revoke-all. - da5136d: Publish OAuth protected resource metadata per workspace (P5-02, RFC 9728) at
/.well-known/oauth-protected-resource/w/{slug}/mcp, and name it in theWWW-Authenticatechallenge of every401on/w/{slug}/mcpwhen OAuth is on, so MCP clients that start without a token discover the authorization server and the workspace's resource themselves. The document and the challenge are identical for known, unknown and disabled workspaces. Nothing changes while OAuth is off. - 6b3c7ee: Add the OAuth 2.1 authorization server for MCP clients (P5-01), off by default: RFC 8414 metadata,
/oauth/authorize,/oauth/token(code, refresh and RFC 7009 revocation) and/oauth/register, with PKCE S256 for every client, RFC 8707 resources bound to one workspace, RFC 9207iss, single-use codes, hash-only token storage in the database and OAuth access tokens accepted on/w/{slug}/mcpthrough the same access resolver as device tokens. New settingsAB_OAUTH,AB_OAUTH_ACCESS_TTLandAB_OAUTH_REFRESH_TTL;AB_OAUTH=onis refused until the consent screen arrives. Log redaction learns the OAuth token shape. - 58b6c02: Add the OAuth token lifecycle (P5-05): refresh tokens rotate on every use with a reuse window (
AB_OAUTH_REFRESH_REUSE_WINDOW, default 30 s, 0-120 s), so a client that refreshes several times in parallel, or repeats a refresh whose answer it lost, keeps its connection and gets the same new tokens; a spent refresh token used again after the window, or at window 0, disconnects the whole device (access and refresh tokens) and writes anoauth.refresh.reuseaudit row without any token. The new tokens kept for the window are stored encrypted under a key derived from the spent token and deleted afterwards. Every refresh, also a repeat inside the window, checks that the device is still connected and that the access still exists. Works onserve(SQLite) and the Worker (D1).
Patch Changes
- b00cc08: OAuth fixes from the Phase 5 review (P5-10). A revocation request no longer renews an unused dynamic registration (it could be kept for 90 days with one request). At the registration cap, registrations nobody used within the hour make room, and at the stored-document cap a cached client document is evicted instead of the new client being refused, so one source can no longer hold either cap for everyone else. A client whose metadata document is temporarily unreachable gets
503or429at the token endpoint instead ofinvalid_client, which made SDK clients discard their tokens and ask for approval again. The OAuth per-address limits count an IPv6 address by its /64 network, as the general limiter does. Redaction now covers a Bearer OAuth token in free text. The admin Connect page no longer calls OAuth a later release. - 882239c: OAuth: at the dynamic-registration cap, the sweep that makes room now always looks at its full slice of registrations (it wraps round to the start of the list), so a newcomer is no longer refused, now and then, while abandoned registrations remain.
v0.4.0
Minor Changes
- 66d6630: Admin panel scaffold (P4-03): a server-rendered, phone-friendly panel at
/adminonserveand on the Worker, built with
Hono JSX under a strict content security policy (no inline script or style, hashed assets with SRI, no framing, Trusted
Types) and a page and form kit with session, role, CSRF and fetch-metadata checks. Changes go through the admin API in
process, so its guards, re-authentication rule and audit rows apply unchanged. Sign-in now lands on/admin(overview and
account pages);/auth/accountredirects to/admin/account, and the sign-in pages share the panel's layout. - 02511c3: Admin screens (P4-04): the panel at
/adminnow manages instances, credentials, workspaces, grants, device tokens and
users for owners and admins, onserveand on the Worker. Every change goes through the admin API as the signed-in person
(same role rules, re-authentication and audit rows); controls appear only when the API would allow them. Agility tokens are
typed once into a password field and never shown again; a new device token is shown once, behind a single-use form handle,
so a reload never makes a second one; origin changes, token replacement, write settings and owner changes ask for a recent
sign-in; the last active owner is protected with a clear message. The admin API gainsGET /tools, and last-owner refusals
carryerror.detail: "last_owner". - a5bafcb: Admin sessions, roles, CSRF and re-authentication (P4-02): a finished sign-in starts a browser session (
__Host-
cookie, only a hash in the database, idle 30 min / absolute 12 h, at most 20 per user, rotated at every sign-in) that the
admin API accepts as the signed-in user, with a CSRF token plusOriginandSec-Fetch-Sitechecks on every change.
Owner-only management and the last-owner rule apply unchanged; high-impact changes (instance origin, key retirement,
owners and admins, credential replacement, write enablement) need a sign-in from the last 10 minutes
(403 reauth_required,/auth/reauth).POST /auth/logout,GET /auth/account,GET /api/admin/v1/session,
POST /api/admin/v1/users/{id}/sessions/revokeandagility-bridge user end-sessions. New settings
AB_SESSION_IDLE_MINUTES,AB_SESSION_MAX_HOURS,AB_SESSION_MAX_PER_USERandAB_LOGOUT_CLEAR_SITE_DATA. - d51cfa1: Audit viewer and usage statistics (P4-05): the panel at
/admingains an audit log (filters for time, kind, outcome, tool or
action, workspace and user; keyset paging; every stored string shown as text; a formula-safe CSV download of the newest 1,000
matching rows) and, for owners and admins, usage statistics (calls, errors, denials, Agility requests and durations per tool,
workspace, grant, user or day). Members see only their own audit rows:GET /api/admin/v1/auditis now open to them and scoped
to their user id by the server (anotheruserIdis refused), and it gains anactionfilter. NewGET /api/admin/v1/audit/stats(owner and admin) aggregates at most the newest 10,000 tool calls of a window and reports when it
stopped there. Storage gainsaudit.statson memory, SQLite and D1, with no schema change. - f8b6cdb: Connect page (P4-06): every grant has a "Connect a client" page in the admin panel (under My access for the person who holds
it, and on the grant for owners and admins) with the workspace's MCP endpoint and a copy-paste configuration for GitHub Copilot
in VS Code, JetBrains and the CLI, Claude Code, Cursor, Windsurf / Devin Desktop and the MCP Inspector. The page never holds a
token; the one-time page that creates a device token repeats the snippets and fills the token in only where a client cannot read
it from a prompt or an environment variable. The client setup guide gains the same remote snippets. - abbcdba: Admin sign-in (P4-01): Google, GitHub, Cloudflare Access and any OpenID Connect provider at
/auth/*, deny by default
with allowlists and optional auto-provisioning, one identity per user. The first owner is created with a single-use
setup link (agility-bridge setup-link,POST /api/admin/v1/setup-links) whose code travels in the URL fragment. New
settingsAB_GOOGLE_*,AB_GITHUB_*,AB_OIDC_*,AB_CF_ACCESS_*,AB_LOGIN_*,AB_RATE_LIMIT_SIGN_INand
AB_IDENTITY_PRIVATE_ADDRESSES; client secrets may come from*_FILE. Browser sessions follow in the next release. - 8281ec2: Member self-service (P4-07): every signed-in person has "My access" in the panel (
/admin/me) to add, check, replace, revoke and
delete their own personal Agility token (checked against Agility at once, never shown again), choose which of their grants
uses it, and issue and revoke their own device tokens, without an administrator handling the secret. Everything goes
through the admin API as the signed-in person: another person's or a shared credential, a grant or device token that is not
theirs, and an instance they have no grant on are refused with no effect. The admin API gainsGET /me/grants,POST /me/grants/{id}/credentialandGET /me/audit; a member may now delete their own personal credential; caps of 10 personal
credentials per person and 20 live device tokens per grant apply to every writer. - a3ff7a3: Move a user to another sign-in provider on purpose (P4-12): owners (and the break-glass admin token) can clear a user's
provider identity withagility-bridge user unbind <user-id> --confirm <email>,POST /users/{id}/unbindor the "Move to
another provider" page of the admin panel, so the next verified sign-in with that email binds again. It needs a recent
sign-in, the user's email typed again and never works on your own account through a session; the clear is one compare-and-set
step, the user's browser sessions end, and the audit log recordsuser.unbindwith the previous provider only. - 4cf065a: Panel follow-ups (P4-15): a disabled user can be enabled again (
POST /api/admin/v1/users/{id}/enable,agility-bridge user enable, an Enable button on the user's page: owners for anyone with a recent sign-in for owners and admins, admins for members);
GET /api/admin/v1/tokensand a Device tokens page list every device token (prefix, label, dates and grant, never a hash), filtered
by person or grant and paged by cursor, with revoke; and an instance's number prefixes are validated on every path and editable
in the panel (Type=PREFIX, one per line). Stored prefixes are not applied to number lookups yet (P4-30).
Patch Changes
- 23ab704: Fixes found by running the admin panel in a real browser (P4-08): a table that scrolls sideways on a narrow screen can now
be reached with the keyboard (it is a focusable, named region), and an admin API request that sends an empty body as JSON
is answered400 bad_requestinstead of500 internal error. - c1334b9: Admin security review (P4-09): setting an instance's TLS trust (a CA file) through the admin API needs an owner with a recent
sign-in, as the panel already said; adding projects, child projects or asset types to a workspace or grant that has write
tools, making a workspace pass-through, and issuing a device token that lasts more than 90 days (or never expires) need a
recent sign-in (owner for the first three); a device token issued while its grant moves to a personal token is revoked; a
credential validation that started before a token replacement can no longer vouch for the new token; the admin API stops
reading a request body at its 64 KiB cap; and audit rows of admin changes now record the role (and previous role), project
and tool lists, write switches, the token id and end, and the instance host. - 5effbc1: Log redaction now also covers the admin sign-in values (P4-11): the CSRF token and
_csrfform field, the PKCE verifier, the
nonce, the setup code, the session secret as an object key, and the OAuthcodein a callback URL's query string. - 827d13e: Updates undici to 7.30.1 for GHSA-xjj6-f33q-929c (a denial of service through a response with too many content encodings).
v0.3.0
Minor Changes
- 177e0e6:
agility-bridge serveand the management commands refuse plain-HTTP instance URLs (alsohttp://127.0.0.1) unless
AB_ALLOW_HTTP_INSTANCES=on, meant for tests and the local synthetic Agility. NewAB_INSTANCE_PRIVATE_ADDRESSES=refuse
accepts only instances at public addresses, checked when an instance is added or retargeted and at every connection.
The stdio mode is unchanged. - ba32352: Master-key status and retirement through the admin API (
GET /keys,POST /keys/{version}/retire, owners; retiring
needs a recent sign-in and is audited), soagility-bridge keys statusandkeys retirework remotely too (for
example against a Cloudflare Worker).agility-bridge init --version <N>writes a key for the next version of a
rotation.keys rotatestays local.
Patch Changes
- eb5d24c: Device tokens are resolved with one database round trip instead of four (the token and the records its access reads
in one batch). - bdcc3e9: Phase 3 review fixes:
keys retire(andPOST /keys/{version}/retire) refuses a version this database never used or a
future one, and keeps the operator's reason in the audit row;AB_INSTANCE_PRIVATE_ADDRESSES=refusealso refuses
IPv4 embedded in IPv6 (IPv4-compatible, SIIT, NAT64, 6to4), site-local IPv6 and the special and documentation IPv4
ranges. - ea56e1e:
agility-bridge servereads its settings and assembles its app through the code the Cloudflare Worker now shares
(no change in behaviour or settings).
v0.2.0
Minor Changes
- 5cc97dc: Admin REST API for
agility-bridge serveat/api/admin/v1(instances, credentials, workspaces, grants, device
tokens, users, audit), enabled by a break-glass token from the newagility-bridge admin-tokencommand
(AB_ADMIN_TOKEN_FILE). Owner/admin/member guards apply to every change, secrets are never returned, and every change
is audited. Reference: docs/reference/admin-api.md. - ced47c2:
agility-bridge servekeeps an audit log of tool calls (asset numbers, masked queries, durations, Agility requests),
refused requests and admin changes — never tokens, request values or Agility content — with a retention of
AB_AUDIT_RETENTION_DAYS(default 90). See docs/guide/audit-log.md. - 7c3b1ad:
agility-bridge backup <file>writes a consistent copy of the server database while it runs, and
agility-bridge restore <backup> <target>checks and restores one; the self-hosting guide covers Node, Docker, reverse
proxies, on-premises CAs, backups and upgrades, and a new guide covers one token per device. - e74b966: Management commands for a server:
agility-bridge init, anduser,instance,credential,workspace,grant,
token,audit tailandkeys status|rotate|retire, either on the local database or through the admin API of
AB_ADMIN_URL, with the same rules and audit. Secrets come from files, the environment, standard input or a hidden
prompt, never from arguments. See docs/guide/cli.md. - b031032: Stored Agility tokens are validated on save, on demand (
agility-bridge credential validate), every
AB_CREDENTIAL_REVALIDATION_HOURSand after repeated refusals; new or changed instance addresses are probed without a
token before use. Clear messages separate a revoked token from a wrong instance address. - dd09769: Optional pass-through workspaces (
AB_PASSTHROUGH=on, off by default): callers send their own Agility token in
X-Agility-Tokenover HTTPS; nothing is stored, every workspace control applies, and they are read-only. Also: the
startup warning forAB_RATE_LIMIT=offis now printed. - cdae959:
agility-bridge servelimits requests per client address, failed authentications per address (valid tokens are never
locked out), and requests per device token and per grant, answering429withRetry-After. Settings:
AB_RATE_LIMIT*andAB_TRUSTED_PROXY_HOPS(docs/guide/configuration.md). - 6d72adf: New
agility-bridge serve: a team server over HTTP that exposes each workspace at/w/<slug>/mcpto per-device
tokens, with SQLite storage, Agility tokens encrypted under a master key (AB_MASTER_KEY_FILE), per-workspace and
per-grant access, and both MCP protocol eras. See docs/guide/secrets.md and docs/guide/access-model.md.
Patch Changes
- 12008ec:
get_assetsand the single-item tools keep only the assets that were asked for: an Agility response that ignores the
number or OID filter can no longer make another asset appear in place of the one requested. - fc15b31: Phase 2 review fixes for
agility-bridge serve: requests whose target is not a plain path (GET https://…) get
400, and pass-through workspaces treat a request as HTTPS only when a trusted proxy says so; a client address out of
failed attempts no longer triggers Agility validations, new pass-through tokens are checked at most
AB_RATE_LIMIT_VALIDATIONS_PER_MINUTEtimes per instance (default 30) and concurrent requests share one check; IPv6
clients are rate-limited per /64; malformedHostheaders are refused; an admin token file reached through a
symbolic link into the data directory is refused; the database refuses to leave the server without an active owner
and to un-revoke anything; only a member can issue device tokens for a grant on their own personal Agility token.
v0.1.0
Minor Changes
- 968ee4e: Add the Agility HTTP client: canonical instance URLs, bounded retries for reads, a per-credential limiter and circuit
breaker, byte-capped responses, preciseAB_AGILITY_*errors and custom CA bundles on Node (AB_CA_FILE). - e388470: Resolve asset numbers, OID tokens and the names people use (Agility projects, teams, members including
me, sprints
includingcurrent, statuses) inside the workspace, rechecking every row; ambiguous names list candidates instead of
guessing, and assets outside the workspace answer exactly like missing ones. - da40f10: Add the discovery tools
whoami,list_projects,list_asset_typesanddescribe_asset_type, so an agent can learn
where it is connected and what it can read before it queries anything. - b2cfa3c: Add
query_assetsandget_assets: read any asset type the workspace allows (stories, defects, tasks, tests, epics,
issues, requests, regression tests, custom fields) with structured filters, bound values, rechecked scope and bound
paging cursors. - ec77f7c: Learn each instance's asset model from
meta.v1: asset types, attribute paths with downcasts and aggregations, number
prefixes, status lists, field tiers and how each type is kept inside the workspace's Agility projects, cached per
instance and charged to the call that loads it. - fdec5c6: Shape every tool result the same way: a summary, notes and data blocks in
table,csvorjson, Agility text inside
an unforgeable untrusted-data envelope, rich text converted to plain text, caps with notices, andstructuredContent
holding exactly the rows shown. - 7d9370b: Add
list_teams,get_team,list_sprintsandlist_membersfor planning context: team workflows, members derived
from assignments, sprints by timing, and members of the workspace's projects without emails unless enabled. - 966dd17: Compile structured reads into safe
query.v1queries: validated fields and filters, every value bound throughwith,
an Agility-project restriction no caller can widen, dead assets excluded, explicit paging with stable sorts, enforced
limits, a UTCtime_rangemodel and bound paging cursors. - 7d8cb78: Serve MCP over stdio:
npx agility-bridgereadsAB_AGILITY_URLandAB_AGILITY_TOKEN(or a YAML file via
AB_CONFIG) from the client's environment, validates everyAB_setting at startup, offers the read tools, explains
how to set itself up when unconfigured, and never accepts secrets as flags. - cac775e: Add
search_work_items,get_work_itemandlist_my_workfor the daily questions: find work by project, team, sprint,
owner or status; open one item with its description, acceptance criteria, children and links; and see your own work by
sprint and status.
Patch Changes
- 2a6958e: Paging cursors bind and re-validate their time window and "now": an edited cursor is refused, and
list_sprintskeeps the first page's "now", so a sprint that starts or ends while you page is neither skipped nor repeated. - 43ff3f7: Ship LICENSE, NOTICE and a README with the trademark disclaimer in the npm package.
- 783d5ff: Paged lists no longer skip or repeat rows when some rows on a page are hidden because they are outside the workspace: cursors count Agility positions, not shown rows.
- 14c14be: Harden the read path from the Phase 1 review: values of related assets outside the workspace are never returned,
member usernames follow the email setting, hostile rich text converts in linear time, multi-type pages never skip or
repeat rows, aggregation values are returned, and YAML references are allowed only in the token. - 5816321: Ship with verifiable releases: npm trusted publishing with provenance, a CycloneDX SBOM, checksums and a Sigstore
attestation on every GitHub Release, and an MCP Registry entry (server.json). - 819ec4c: Tool arguments state their defaults in the schema (shown by clients and in the generated tool reference) instead of in
prose, and bounded lists declaretruncatedin their output schemas.