diff --git a/.gitignore b/.gitignore index 4c053d3..b16f274 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ Screenshot*.png # are published. This repository has a public remote, so the whole directory is # ignored rather than named file by file. docs/ + +# Working notes and local skills, not part of what this repo ships. +.claude/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2243b60..e57c560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,40 @@ Notable changes, newest first. Dates are the day the work merged. +## 0.1.3 - 2026-08-28 + +### Added + +- Gmail can write. `send_message`, `modify_message` and `trash_message` join the + four reads. The scope moves from `gmail.readonly` to `gmail.modify`, so every + existing Google connection re-consents once (#86). +- Google Calendar can write. `create_event`, `update_event` and `delete_event`, + on `calendar.events` (#86). +- GitHub can answer a review and close what it opened (#85). +- An api key is called against the vendor before it is stored, so a typo fails + at connect time rather than on the first tool call (#82, thanks + @mikemikimike). +- A manifest argument can hold a list of objects, which is what `attendees` on a + calendar event needed (#92). + +### Fixed + +- A Google 403 from a scope the grant never got now says `reauth_required` + instead of `upstream_error`, so the caller is told to reconnect. A 403 from an + API that was never enabled still reports upstream, because reconnecting does + not fix that one (#89). +- The key check runs under a deadline, and the executor maps its failures apart + from upstream ones (#91). + +### Changed + +- Calendar asks for `calendar.calendarlist.readonly` where it used to ask for + `calendar.readonly`. `calendar.events` already covers every event tool, + reads included, and the only call outside it is `list_calendars` on + `/users/me/calendarList`. The old pair read every calendar wholesale to + satisfy one list call. +- Hosted links point at `selat.dev`. The old host still answers (#93). + ## 0.1.2 - 2026-08-23 ### Added diff --git a/package.json b/package.json index 332d723..892cf5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fajarhide/selat", - "version": "0.1.2", + "version": "0.1.3", "description": "The unified tool gateway for AI agents: one credential, every upstream, over MCP and REST.", "type": "module", "license": "Apache-2.0", diff --git a/src/adapters/providers/gcalendar.ts b/src/adapters/providers/gcalendar.ts index edba2d7..a20b459 100644 --- a/src/adapters/providers/gcalendar.ts +++ b/src/adapters/providers/gcalendar.ts @@ -13,7 +13,12 @@ export const gcalendarManifest: ProviderManifest = { // which is the only thing list_calendars calls, so dropping it would break a // tool that works today. scopes: [ - 'https://www.googleapis.com/auth/calendar.readonly', + // calendar.events covers reads as well as writes, and every event tool + // goes through /calendars/{id}/events. The only call outside that is + // list_calendars on /users/me/calendarList, which is all the second + // scope grants. calendar.readonly would cover both and is what this + // used to ask for, but it reads every calendar wholesale for one list. + 'https://www.googleapis.com/auth/calendar.calendarlist.readonly', 'https://www.googleapis.com/auth/calendar.events', ], auth: { type: 'bearer' }, diff --git a/test/providers/google-workspace.test.ts b/test/providers/google-workspace.test.ts index 66ff9f0..f157dbe 100644 --- a/test/providers/google-workspace.test.ts +++ b/test/providers/google-workspace.test.ts @@ -57,17 +57,20 @@ describe('one google application, three prefixes', () => { expect(new Set(scopes)).toEqual( new Set([ 'https://www.googleapis.com/auth/gmail.modify', - 'https://www.googleapis.com/auth/calendar.readonly', + 'https://www.googleapis.com/auth/calendar.calendarlist.readonly', 'https://www.googleapis.com/auth/calendar.events', 'https://www.googleapis.com/auth/drive', ]), ) }) - it('keeps calendar.readonly beside calendar.events, because list_calendars needs it', () => { - // calendar.events does not grant calendarList.list, so dropping the - // readonly scope would break a tool that works today. - expect(gcal.scopes).toContain('https://www.googleapis.com/auth/calendar.readonly') + it('pairs calendar.events with the calendar list, and never asks for calendar.readonly', () => { + // calendar.events covers every event tool, reads included, but not + // calendarList.list. calendar.readonly would cover both and is what this + // used to ask for. It reads every calendar wholesale to satisfy one list + // call, which is the kind of ask a Google reviewer rejects. + expect(gcal.scopes).toContain('https://www.googleapis.com/auth/calendar.calendarlist.readonly') + expect(gcal.scopes).not.toContain('https://www.googleapis.com/auth/calendar.readonly') }) })