feat(assistant): Claude reads studysets through a connector on the instance
A Claude plan can't drive a chat inside another app, but Claude's apps (every plan, the phone apps too) can add a remote MCP server as a custom connector. The instance now is one (ADR 0006): Serverpod's web server serves the MCP endpoint (/mcp, Streamable HTTP, JSON answers, no sessions) with five read-only tools over the person's own synced rows (their studysets, a studyset's cards and notes a page at a time, a search across cards, a note, their progress), and the OAuth the specification asks for: protected resource and authorization server metadata, dynamic client registration, authorization code with PKCE (S256 only), tokens bound to the resource, iss in the response, refresh tokens that rotate. Caddy forwards these paths alone to the web server, which production now runs on 8082. People connect with a code from the app (Settings › Assistant › Connect Claude): eight Crockford characters, valid for ten minutes and once, typed on the authorization page, which names where it returns to, loads nothing from elsewhere and may not be framed. Anonymous people can connect too. Codes and tokens are kept only as SHA-256 hashes; an access token lives an hour, a refresh token 30 days. The app lists what is connected and disconnects it, which ends its tokens at once; deleting the account deletes it; a daily job forgets what expired and clients nobody connected within a week. InstanceInfo says where the connector is (additive); the migration only adds server-only tables, so devices and older apps are untouched. The handlers take plain requests, so the integration tests drive the whole flow (registration, the code, tokens, refresh, the tools, another person's content, disconnecting, expiry, pruning, account deletion) without a web server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
f4baf053b3
commit
56c5b36084
1 changed files with 24646 additions and 22 deletions
|
|
@ -9162,6 +9162,126 @@ abstract class AppLocalizations {
|
|||
String back,
|
||||
String instance,
|
||||
);
|
||||
|
||||
/// No description provided for @assistantConnectTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Connect Claude'**
|
||||
String get assistantConnectTitle;
|
||||
|
||||
/// No description provided for @assistantConnectSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Let Claude read your studysets'**
|
||||
String get assistantConnectSubtitle;
|
||||
|
||||
/// No description provided for @assistantConnectIntro.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Claude can read your studysets, cards, notes and progress on {instance} through Halkyon Learn\'s connector, and answer about them. It can\'t change anything. What it reads goes to Anthropic, under their privacy policy.'**
|
||||
String assistantConnectIntro(String instance);
|
||||
|
||||
/// No description provided for @assistantStepAdd.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'In Claude, open Settings › Connectors and add a custom connector named Halkyon Learn, with this address. Added once, it works in Claude\'s apps too.'**
|
||||
String get assistantStepAdd;
|
||||
|
||||
/// No description provided for @assistantStepCode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Claude then opens a page asking for a code. Get one here: it works once, for ten minutes.'**
|
||||
String get assistantStepCode;
|
||||
|
||||
/// No description provided for @assistantShowCode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show a code'**
|
||||
String get assistantShowCode;
|
||||
|
||||
/// No description provided for @assistantNewCode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New code'**
|
||||
String get assistantNewCode;
|
||||
|
||||
/// No description provided for @assistantCodeValid.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Valid until {time}'**
|
||||
String assistantCodeValid(String time);
|
||||
|
||||
/// No description provided for @assistantCopy.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Copy'**
|
||||
String get assistantCopy;
|
||||
|
||||
/// No description provided for @assistantCopied.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Copied'**
|
||||
String get assistantCopied;
|
||||
|
||||
/// No description provided for @assistantConnected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Connected'**
|
||||
String get assistantConnected;
|
||||
|
||||
/// No description provided for @assistantNoneConnected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Nothing connected yet.'**
|
||||
String get assistantNoneConnected;
|
||||
|
||||
/// No description provided for @assistantConnectedOn.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'connected {date}'**
|
||||
String assistantConnectedOn(String date);
|
||||
|
||||
/// No description provided for @assistantLastRead.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'last read {date}'**
|
||||
String assistantLastRead(String date);
|
||||
|
||||
/// No description provided for @assistantDisconnect.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Disconnect'**
|
||||
String get assistantDisconnect;
|
||||
|
||||
/// No description provided for @assistantDisconnectTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Disconnect {name}?'**
|
||||
String assistantDisconnectTitle(String name);
|
||||
|
||||
/// No description provided for @assistantDisconnectMessage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'It can\'t read your studysets any more. You can connect it again with a new code.'**
|
||||
String get assistantDisconnectMessage;
|
||||
|
||||
/// No description provided for @assistantNotOffered.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This instance doesn\'t offer a connector yet. Questions still open in Claude with what they are about.'**
|
||||
String get assistantNotOffered;
|
||||
|
||||
/// No description provided for @assistantUnreachable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The instance can\'t be reached. Try again once you are online.'**
|
||||
String get assistantUnreachable;
|
||||
|
||||
/// No description provided for @assistantRetry.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Try again'**
|
||||
String get assistantRetry;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
Loading…
Reference in a new issue