utn-mcp 0.8.3 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -3
- package/build/aula-virtual/client/client.d.ts +97 -0
- package/build/aula-virtual/client/credentials.d.ts +10 -0
- package/build/aula-virtual/client/link.d.ts +21 -0
- package/build/aula-virtual/client/local-session.d.ts +23 -0
- package/build/aula-virtual/client/moodle-api.d.ts +25 -0
- package/build/aula-virtual/client/regionals.d.ts +47 -0
- package/build/aula-virtual/client/session.d.ts +12 -0
- package/build/aula-virtual/client/sso-login.d.ts +60 -0
- package/build/aula-virtual/client/stored-session.d.ts +32 -0
- package/build/aula-virtual/client/types.d.ts +173 -0
- package/build/aula-virtual/format/actions.d.ts +57 -0
- package/build/aula-virtual/format/activities.d.ts +86 -0
- package/build/aula-virtual/format/address.d.ts +36 -0
- package/build/aula-virtual/format/fields.d.ts +11 -0
- package/build/aula-virtual/format/files.d.ts +32 -0
- package/build/aula-virtual/format/navigate.d.ts +213 -0
- package/build/aula-virtual/format/study-pack.d.ts +33 -0
- package/build/aula-virtual/format/submission.d.ts +40 -0
- package/build/aula-virtual/login.d.ts +6 -0
- package/build/aula-virtual/tools.d.ts +4 -0
- package/build/autogestion/client/catalog.d.ts +20 -0
- package/build/autogestion/client/catalog.js +22 -0
- package/build/autogestion/client/connector.d.ts +71 -0
- package/build/autogestion/client/connector.js +31 -0
- package/build/autogestion/client/credentials.d.ts +5 -0
- package/build/autogestion/client/credentials.js +40 -0
- package/build/autogestion/client/install.d.ts +19 -0
- package/build/autogestion/client/install.js +91 -0
- package/build/autogestion/client/installed-systems.d.ts +7 -0
- package/build/autogestion/client/installed-systems.js +58 -0
- package/build/autogestion/client/local-session.d.ts +17 -0
- package/build/autogestion/client/local-session.js +37 -0
- package/build/autogestion/client/session.d.ts +18 -0
- package/build/autogestion/client/session.js +1 -0
- package/build/autogestion/client/stored-session.d.ts +42 -0
- package/build/autogestion/client/stored-session.js +126 -0
- package/build/autogestion/client/types.d.ts +49 -0
- package/build/autogestion/client/types.js +1 -0
- package/build/autogestion/format/correlatividades.d.ts +4 -0
- package/build/autogestion/format/correlatividades.js +4 -0
- package/build/autogestion/format/estado-academico.d.ts +4 -0
- package/build/autogestion/format/estado-academico.js +4 -0
- package/build/autogestion/format/examenes.d.ts +3 -0
- package/build/autogestion/format/examenes.js +8 -0
- package/build/autogestion/format/notas-parciales.d.ts +3 -0
- package/build/autogestion/format/notas-parciales.js +8 -0
- package/build/autogestion/link-page.d.ts +7 -0
- package/build/autogestion/link-page.js +62 -0
- package/build/autogestion/login.d.ts +11 -0
- package/build/autogestion/login.js +59 -0
- package/build/autogestion/tools.d.ts +3 -0
- package/build/autogestion/tools.js +73 -0
- package/build/http.d.ts +26 -0
- package/build/http.js +7 -2
- package/build/index.d.ts +2 -0
- package/build/index.js +35 -4
- package/build/oauth/access.d.ts +18 -0
- package/build/oauth/admin.d.ts +8 -0
- package/build/oauth/admin.js +1 -1
- package/build/oauth/pending-login.d.ts +20 -0
- package/build/oauth/redirects.d.ts +20 -0
- package/build/oauth/server.d.ts +22 -0
- package/build/oauth/server.js +1 -1
- package/build/server.d.ts +11 -0
- package/build/server.js +19 -2
- package/build/shared/documents/index.d.ts +33 -0
- package/build/shared/documents/merge.d.ts +32 -0
- package/build/shared/documents/office.d.ts +9 -0
- package/build/shared/documents/pdf.d.ts +2 -0
- package/build/shared/documents/png.d.ts +2 -0
- package/build/shared/documents/zip.d.ts +18 -0
- package/build/shared/expiring.d.ts +26 -0
- package/build/{oauth → shared}/expiring.js +4 -3
- package/build/shared/html.d.ts +6 -0
- package/build/shared/html.js +7 -1
- package/build/shared/http.d.ts +6 -0
- package/build/shared/input.d.ts +7 -0
- package/build/shared/load-env.d.ts +1 -0
- package/build/shared/logger.d.ts +5 -0
- package/build/shared/pages.d.ts +6 -0
- package/build/shared/paths.d.ts +10 -0
- package/build/shared/store/session-store.d.ts +77 -0
- package/build/shared/store/sqlite-store.d.ts +36 -0
- package/build/shared/uploads.d.ts +62 -0
- package/package.json +8 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Página mínima con el estilo del sitio del proyecto: se abre desde el celu, así que nada más que lo necesario.
|
|
3
|
+
* `kicker` es la etiqueta chica arriba del título.
|
|
4
|
+
*/
|
|
5
|
+
export declare function page(title: string, body: string, kicker?: string): string;
|
|
6
|
+
export declare const escapeHtml: (s: string) => string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Carpeta de configuración: UTN_MCP_HOME, %APPDATA% en Windows, o XDG/~/.config en Linux y macOS. */
|
|
2
|
+
export declare function configDir(): string;
|
|
3
|
+
/** Token de Moodle del modo local. */
|
|
4
|
+
export declare const sessionFile: () => string;
|
|
5
|
+
/** Base de usuarios, tokens y sesiones del modo HTTP: UTN_MCP_DB o un SQLite en la carpeta de configuración. */
|
|
6
|
+
export declare const storeFile: () => string;
|
|
7
|
+
/** Clave con la que se cifran las sesiones en la base, si no viene en UTN_MCP_KEY. */
|
|
8
|
+
export declare const storeKeyFile: () => string;
|
|
9
|
+
/** Dónde quedan los archivos que arma el servidor (packs de estudio): UTN_MCP_EXPORTS o exports/ en la configuración. */
|
|
10
|
+
export declare function exportsDir(): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dónde viven los usuarios del modo HTTP, sus tokens y sus sesiones en cada plataforma. El servidor
|
|
3
|
+
* MCP lee (y guarda la sesión cuando la renueva); los usuarios y tokens los crea otro proceso: el
|
|
4
|
+
* login por OAuth de la versión hosteada o `utn-mcp login --http` en local.
|
|
5
|
+
*/
|
|
6
|
+
export interface SessionStore {
|
|
7
|
+
/**
|
|
8
|
+
* Id del usuario con ese nombre en esa regional; lo crea si no existe. Con `email`, se lo actualiza.
|
|
9
|
+
* Dos regionales pueden tener el mismo nombre de usuario para personas distintas.
|
|
10
|
+
*/
|
|
11
|
+
findOrCreateUser(regional: string, utnUsername: string, email?: string): Promise<number>;
|
|
12
|
+
findUser(userId: number): Promise<StoredUser | null>;
|
|
13
|
+
findUserByEmail(email: string): Promise<StoredUser | null>;
|
|
14
|
+
/** Todos los usuarios, los más nuevos primero. */
|
|
15
|
+
listUsers(): Promise<ListedUser[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Emite un token para el usuario y devuelve el valor en claro: se muestra una sola vez, en la base
|
|
18
|
+
* queda solo el hash. Sin `ttlSeconds` no vence (el token fijo del modo local).
|
|
19
|
+
*/
|
|
20
|
+
issueToken(userId: number, options: {
|
|
21
|
+
kind: TokenKind;
|
|
22
|
+
clientId?: string;
|
|
23
|
+
ttlSeconds?: number;
|
|
24
|
+
}): Promise<string>;
|
|
25
|
+
/** El token si es de ese tipo y está vigente; si no, null. */
|
|
26
|
+
findToken(token: string, kind: TokenKind): Promise<IssuedToken | null>;
|
|
27
|
+
revokeToken(token: string): Promise<void>;
|
|
28
|
+
/** La sesión del usuario en una plataforma, tal como la guardó: cada plataforma decide su forma. */
|
|
29
|
+
loadSession(userId: number, platform: string): Promise<string | null>;
|
|
30
|
+
saveSession(userId: number, platform: string, data: string): Promise<void>;
|
|
31
|
+
/** Cuándo se guardó por última vez la sesión, en segundos desde epoch; null si no hay. */
|
|
32
|
+
sessionUpdatedAt(userId: number, platform: string): Promise<number | null>;
|
|
33
|
+
/** Las regionales que alguien ya usó en este servidor sin que estuvieran verificadas en el catálogo. */
|
|
34
|
+
listVerifiedRegionals(): Promise<string[]>;
|
|
35
|
+
/** true si esta llamada la verificó: nadie lo había hecho antes. */
|
|
36
|
+
markRegionalVerified(regional: string): Promise<boolean>;
|
|
37
|
+
/** Clientes OAuth registrados por registro dinámico (RFC 7591). */
|
|
38
|
+
saveClient(client: RegisteredClient): Promise<void>;
|
|
39
|
+
findClient(clientId: string): Promise<RegisteredClient | null>;
|
|
40
|
+
/** Los clientes que todavía tienen un token vigente del usuario: las apps que tiene conectadas. */
|
|
41
|
+
listConnectedClients(userId: number): Promise<ConnectedClient[]>;
|
|
42
|
+
/** Borra todos los tokens del usuario para ese cliente: la app queda desconectada. */
|
|
43
|
+
revokeClient(userId: number, clientId: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export interface ConnectedClient {
|
|
46
|
+
clientId: string;
|
|
47
|
+
/** El que mandó la app al registrarse; null para el token fijo de `utn-mcp login --http`. */
|
|
48
|
+
clientName: string | null;
|
|
49
|
+
/** Cuándo se emitió su token más nuevo, en segundos desde epoch: los refresh rotan con el uso. */
|
|
50
|
+
lastTokenAt: number;
|
|
51
|
+
}
|
|
52
|
+
export interface StoredUser {
|
|
53
|
+
id: number;
|
|
54
|
+
/** El id de su regional (`frba`, `frt`...): su aula y, junto con el nombre, su identidad. */
|
|
55
|
+
regional: string;
|
|
56
|
+
utnUsername: string;
|
|
57
|
+
/** El institucional, el que se usa para decidir el acceso; null en usuarios de antes de guardarlo. */
|
|
58
|
+
email: string | null;
|
|
59
|
+
}
|
|
60
|
+
export interface ListedUser extends StoredUser {
|
|
61
|
+
/** Cuándo entró por primera vez, en segundos desde epoch. */
|
|
62
|
+
createdAt: number;
|
|
63
|
+
}
|
|
64
|
+
/** Un cliente público: no tiene secreto, se autentica con PKCE. */
|
|
65
|
+
export interface RegisteredClient {
|
|
66
|
+
clientId: string;
|
|
67
|
+
clientName: string | null;
|
|
68
|
+
redirectUris: string[];
|
|
69
|
+
}
|
|
70
|
+
/** `access` va en cada request al MCP; `refresh` solo sirve para pedir un `access` nuevo. */
|
|
71
|
+
export type TokenKind = "access" | "refresh";
|
|
72
|
+
export interface IssuedToken {
|
|
73
|
+
userId: number;
|
|
74
|
+
clientId: string | null;
|
|
75
|
+
/** En segundos desde epoch; null si no vence. */
|
|
76
|
+
expiresAt: number | null;
|
|
77
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ConnectedClient, IssuedToken, ListedUser, RegisteredClient, SessionStore, StoredUser, TokenKind } from "./session-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* El SessionStore sobre SQLite. Las sesiones van cifradas con AES-256-GCM: tienen el token de Moodle,
|
|
4
|
+
* que alcanza para entrar al aula como el usuario. Los tokens se guardan
|
|
5
|
+
* hasheados, así que una copia de la base no sirve para usar el servidor.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SqliteStore implements SessionStore {
|
|
8
|
+
private readonly db;
|
|
9
|
+
private readonly key;
|
|
10
|
+
private constructor();
|
|
11
|
+
/** `node:sqlite` llega con Node 22.13; se importa acá para que el modo STDIO siga andando en Node 20. */
|
|
12
|
+
static open(path?: string): Promise<SqliteStore>;
|
|
13
|
+
findOrCreateUser(regional: string, utnUsername: string, email?: string): Promise<number>;
|
|
14
|
+
findUserByEmail(email: string): Promise<StoredUser | null>;
|
|
15
|
+
listUsers(): Promise<ListedUser[]>;
|
|
16
|
+
findUser(userId: number): Promise<StoredUser | null>;
|
|
17
|
+
issueToken(userId: number, options: {
|
|
18
|
+
kind: TokenKind;
|
|
19
|
+
clientId?: string;
|
|
20
|
+
ttlSeconds?: number;
|
|
21
|
+
}): Promise<string>;
|
|
22
|
+
findToken(token: string, kind: TokenKind): Promise<IssuedToken | null>;
|
|
23
|
+
revokeToken(token: string): Promise<void>;
|
|
24
|
+
loadSession(userId: number, platform: string): Promise<string | null>;
|
|
25
|
+
saveSession(userId: number, platform: string, data: string): Promise<void>;
|
|
26
|
+
listVerifiedRegionals(): Promise<string[]>;
|
|
27
|
+
markRegionalVerified(regional: string): Promise<boolean>;
|
|
28
|
+
saveClient(client: RegisteredClient): Promise<void>;
|
|
29
|
+
findClient(clientId: string): Promise<RegisteredClient | null>;
|
|
30
|
+
sessionUpdatedAt(userId: number, platform: string): Promise<number | null>;
|
|
31
|
+
listConnectedClients(userId: number): Promise<ConnectedClient[]>;
|
|
32
|
+
revokeClient(userId: number, clientId: string): Promise<void>;
|
|
33
|
+
/** iv + tag + texto cifrado, en un solo blob. */
|
|
34
|
+
private encrypt;
|
|
35
|
+
private decrypt;
|
|
36
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface UploadedFile {
|
|
2
|
+
name: string;
|
|
3
|
+
bytes: Uint8Array;
|
|
4
|
+
}
|
|
5
|
+
/** Lo que acepta una subida; lo pone la plataforma que la abre. */
|
|
6
|
+
export interface UploadLimits {
|
|
7
|
+
maxFiles?: number;
|
|
8
|
+
/** Por archivo. */
|
|
9
|
+
maxBytes: number;
|
|
10
|
+
/** En minúscula y con punto (".pdf"); sin lista, cualquiera. */
|
|
11
|
+
extensions?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* De dónde saca el servidor los archivos que el usuario quiere subir a una plataforma: en STDIO, del
|
|
15
|
+
* disco, que es el suyo; en HTTP, de una subida, porque el servidor puede estar en otra máquina.
|
|
16
|
+
* `owner` es el usuario de la request: una subida solo la puede usar quien la abrió.
|
|
17
|
+
*/
|
|
18
|
+
export type FileSource = {
|
|
19
|
+
local: true;
|
|
20
|
+
} | {
|
|
21
|
+
local: false;
|
|
22
|
+
inbox: UploadInbox;
|
|
23
|
+
owner: string;
|
|
24
|
+
};
|
|
25
|
+
interface Upload {
|
|
26
|
+
owner: string;
|
|
27
|
+
title: string;
|
|
28
|
+
limits: UploadLimits;
|
|
29
|
+
/** null mientras no se subió nada. */
|
|
30
|
+
files: UploadedFile[] | null;
|
|
31
|
+
expiresAt: number;
|
|
32
|
+
}
|
|
33
|
+
/** Tope de una subida entera, en bytes: los archivos quedan en memoria hasta que se entregan. */
|
|
34
|
+
export declare const MAX_UPLOAD_BYTES: number;
|
|
35
|
+
/**
|
|
36
|
+
* Links de un solo uso para recibir archivos por HTTP: los sube el agente con `curl` si tiene
|
|
37
|
+
* terminal, o el usuario desde el navegador. Los archivos quedan en memoria hasta que una tool los
|
|
38
|
+
* toma o el link vence; si el servidor se reinicia, se pierden y hay que pedir otro link.
|
|
39
|
+
*/
|
|
40
|
+
export declare class UploadInbox {
|
|
41
|
+
private readonly baseUrl;
|
|
42
|
+
private readonly uploads;
|
|
43
|
+
constructor(baseUrl: string);
|
|
44
|
+
open(owner: string, title: string, limits: UploadLimits): {
|
|
45
|
+
id: string;
|
|
46
|
+
url: string;
|
|
47
|
+
expiresAt: string;
|
|
48
|
+
};
|
|
49
|
+
/** La subida para mostrar su formulario, o undefined si no existe o venció. */
|
|
50
|
+
get(id: string): Pick<Upload, "title" | "limits" | "files"> | undefined;
|
|
51
|
+
/** Guarda los archivos de una subida; cada link acepta una sola. */
|
|
52
|
+
receive(id: string, files: UploadedFile[]): void;
|
|
53
|
+
/** Los archivos que se subieron, para entregarlos. No los borra: si la entrega falla, se reintenta. */
|
|
54
|
+
take(id: string, owner: string): UploadedFile[];
|
|
55
|
+
close(id: string): void;
|
|
56
|
+
private sweep;
|
|
57
|
+
}
|
|
58
|
+
/** Cantidad, tamaño, tipo y nombres repetidos, que Moodle no acepta. */
|
|
59
|
+
export declare function checkFiles(files: UploadedFile[], limits: UploadLimits): void;
|
|
60
|
+
/** Tamaño para personas: en KB por debajo del mega, para que un archivo chico no diga "0.0 MB". */
|
|
61
|
+
export declare const formatSize: (bytes: number) => string;
|
|
62
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "utn-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Este es el Model Context Protocol server NO OFICIAL de la UTN para buscar informacion o archivos en el aula virtual.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -55,5 +55,12 @@
|
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=20"
|
|
58
|
+
},
|
|
59
|
+
"exports": {
|
|
60
|
+
".": "./build/index.js",
|
|
61
|
+
"./autogestion": {
|
|
62
|
+
"types": "./build/autogestion/client/connector.d.ts",
|
|
63
|
+
"default": "./build/autogestion/client/connector.js"
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
}
|