# @eduware/oneroster

Type-safe TypeScript SDK for the OneRoster API with LMS-specific client profiles.

<div align="left">
    <a href="https://www.npmjs.com/package/@eduware/oneroster">
        <img src="https://img.shields.io/npm/v/@eduware/oneroster.svg" alt="npm version" />
    </a>
    <a href="https://www.npmjs.com/package/@eduware/oneroster">
        <img src="https://img.shields.io/npm/dm/@eduware/oneroster.svg" alt="npm downloads" />
    </a>
    <a href="https://opensource.org/licenses/MIT">
        <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" />
    </a>
    <a href="#">
        <img src="https://img.shields.io/badge/OneRoster-v1.1%20%7C%20v1.2-green.svg" alt="OneRoster v1.1 | v1.2" />
    </a>
    <a href="#">
        <img src="https://img.shields.io/badge/TypeScript-5.0+-blue.svg" alt="TypeScript 5.0+" />
    </a>
</div>

## Features

- **OneRoster v1.1 & v1.2 support** - Complete implementation of both 1EdTech OneRoster specifications
- **LMS-specific profiles** - Pre-built profiles for ClassLink with type-safe method restrictions
- **Custom profiles** - Create your own profiles using TypeScript's `Pick<>` types
- **OAuth 2.0 & OAuth 1.0a authentication** - OAuth 2.0 for v1.2 (default), OAuth 1.0a for v1.1
- **Automatic pagination** - Async iterables for seamless data fetching
- **TypeScript-first** - Full type safety with comprehensive type definitions
- **Tree-shakeable** - Standalone functions for optimal bundle sizes

## Installation

```bash
# npm
npm add @eduware/oneroster

# pnpm
pnpm add @eduware/oneroster

# yarn
yarn add @eduware/oneroster zod

# bun
bun add @eduware/oneroster
```

> **Note:** Yarn does not install peer dependencies automatically. You will need to install `zod` manually.

## Quick Start

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-oneroster-server.com',
    security: {
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
        tokenURL: 'https://your-oneroster-server.com/oauth/token',
    },
});

// Fetch all users with automatic pagination
const result = await client.usersManagement.getAllUsers({});

for await (const page of result) {
    console.log(page.users);
}
```

## LMS Profiles

The SDK provides LMS-specific client profiles that restrict available methods to match what each provider supports. This gives you compile-time safety and prevents calling unsupported endpoints.

### ClassLink Client

ClassLink provides read-only rostering data. The `ClassLink` client is typed to only expose supported methods:

```typescript
import { ClassLink } from '@eduware/oneroster/clients';

const client = new ClassLink({
    serverURL: 'https://example.classlink.com',
    security: {
        authType: 'bearer',
        bearerToken: process.env.CLASSLINK_BEARER_TOKEN!,
    },
});

// ✅ Read operations work
const users = await client.usersManagement.getAllUsers({});
const schools = await client.schoolsManagement.getAllSchools({});

// ❌ Write operations are not available (TypeScript error)
// client.usersManagement.createUser({});  // Error: Property 'createUser' does not exist
```

### Full Client

For full OneRoster access with all methods available:

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-oneroster-server.com',
    security: {
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
        tokenURL: 'https://your-oneroster-server.com/oauth/token',
    },
});

// All operations available
const users = await client.usersManagement.getAllUsers({});
await client.usersManagement.createUser({
    user: {
        /* ... */
    },
});
```

### Custom Profiles

Create your own profiles by defining an interface that uses `Pick<>` to select specific methods:

```typescript
import { OneRoster } from '@eduware/oneroster';
import type { UsersManagement, ClassesManagement } from '@eduware/oneroster/sdk';
import type { OneRosterClient } from '@eduware/oneroster/clients';

// Define a custom profile with only the methods you need
interface IMyCustomProfile {
    usersManagement: Pick<UsersManagement, 'getAllUsers' | 'getUser'>;
    classesManagement: Pick<ClassesManagement, 'getAllClasses' | 'getClass'>;
}

// Create a typed client
type MyCustomClient = OneRosterClient<IMyCustomProfile>;

const client: MyCustomClient = new OneRoster({
    serverURL: 'https://your-server.com',
    security: {
        /* ... */
    },
});

// Only picked methods are available
const users = await client.usersManagement.getAllUsers({});
// client.usersManagement.createUser({});  // TypeScript error!
```

## Management Modules & LMS Support

The SDK provides management modules for all OneRoster resources. Each module supports CRUD (Create, Read, Update, Delete) operations as defined by the OneRoster specification. However, **not all LMS providers support all operations** - some providers offer read-only access to rostering data.

> **⚠️ Important:** The availability of operations depends on your LMS provider's implementation. The table below shows which operations are supported by each LMS. Operations marked with ❌ will result in API errors if called against providers that don't support them.

### Management Modules Overview

| Module                               | Description                                | Primary Resources                       |
| ------------------------------------ | ------------------------------------------ | --------------------------------------- |
| `usersManagement`                    | User accounts (students, teachers, admins) | Users                                   |
| `studentsManagement`                 | Student-specific operations                | Users (role: student)                   |
| `teachersManagement`                 | Teacher-specific operations                | Users (role: teacher)                   |
| `classesManagement`                  | Class/section management                   | Classes                                 |
| `coursesManagement`                  | Course catalog management                  | Courses                                 |
| `enrollmentsManagement`              | Class enrollment records                   | Enrollments                             |
| `schoolsManagement`                  | School/organization management             | Orgs (type: school)                     |
| `organizationsManagement`            | All organization types                     | Orgs                                    |
| `academicSessionsManagement`         | School years, semesters, terms             | Academic Sessions                       |
| `termsManagement`                    | Term-specific operations                   | Academic Sessions (type: term)          |
| `gradingPeriodsManagement`           | Grading period management                  | Academic Sessions (type: gradingPeriod) |
| `demographicsManagement`             | Student demographic data                   | Demographics                            |
| `lineItemsManagement`                | Gradebook assignments                      | Line Items                              |
| `resultsManagement`                  | Student grades/scores                      | Results                                 |
| `categoriesManagement`               | Grade categories                           | Categories                              |
| `scoreScalesManagement`              | Grading scales                             | Score Scales                            |
| `resourcesManagement`                | Learning resources                         | Resources                               |
| `courseComponentsManagement`         | Course modules/units                       | Course Components                       |
| `courseComponentResourcesManagement` | Resources linked to components             | Component Resources                     |
| `assessmentLineItemsManagement`      | Assessment definitions                     | Assessment Line Items                   |
| `assessmentResultsManagement`        | Assessment scores                          | Assessment Results                      |

### CRUD Operations by Module

| Module                               | Create | Read | Update | Delete |
| ------------------------------------ | :----: | :--: | :----: | :----: |
| `usersManagement`                    |   ✅   |  ✅  |   ✅   |   ✅   |
| `studentsManagement`                 |   —    |  ✅  |   —    |   —    |
| `teachersManagement`                 |   —    |  ✅  |   —    |   —    |
| `classesManagement`                  |   ✅   |  ✅  |   ✅   |   ✅   |
| `coursesManagement`                  |   ✅   |  ✅  |   ✅   |   ✅   |
| `enrollmentsManagement`              |   ✅   |  ✅  |   ✅   |   ✅   |
| `schoolsManagement`                  |   ✅   |  ✅  |   ✅   |   ✅   |
| `organizationsManagement`            |   ✅   |  ✅  |   ✅   |   ✅   |
| `academicSessionsManagement`         |   ✅   |  ✅  |   ✅   |   ✅   |
| `termsManagement`                    |   —    |  ✅  |   —    |   —    |
| `gradingPeriodsManagement`           |   ✅   |  ✅  |   ✅   |   ✅   |
| `demographicsManagement`             |   ✅   |  ✅  |   ✅   |   ✅   |
| `lineItemsManagement`                |   ✅   |  ✅  |   ✅   |   ✅   |
| `resultsManagement`                  |   ✅   |  ✅  |   ✅   |   ✅   |
| `categoriesManagement`               |   ✅   |  ✅  |   ✅   |   ✅   |
| `scoreScalesManagement`              |   ✅   |  ✅  |   ✅   |   ✅   |
| `resourcesManagement`                |   ✅   |  ✅  |   ✅   |   ✅   |
| `courseComponentsManagement`         |   ✅   |  ✅  |   ✅   |   ✅   |
| `courseComponentResourcesManagement` |   ✅   |  ✅  |   ✅   |   ✅   |
| `assessmentLineItemsManagement`      |   ✅   |  ✅  |   ✅   |   ✅   |
| `assessmentResultsManagement`        |   ✅   |  ✅  |   ✅   |   ✅   |

_Note: — indicates the module uses parent module operations (e.g., students/teachers use usersManagement for CUD)_

### LMS Provider Support

Different LMS providers implement different subsets of the OneRoster specification. Use this table to understand what's available for your provider:

| Module                               | OneRoster (Full) | ClassLink |
| ------------------------------------ | :--------------: | :-------: |
| **Core Rostering**                   |                  |           |
| `usersManagement`                    |     ✅ CRUD      |  ✅ Read  |
| `studentsManagement`                 |     ✅ Read      |  ✅ Read  |
| `teachersManagement`                 |     ✅ Read      |  ✅ Read  |
| `classesManagement`                  |     ✅ CRUD      |  ✅ Read  |
| `coursesManagement`                  |     ✅ CRUD      |  ✅ Read  |
| `enrollmentsManagement`              |     ✅ CRUD      |  ✅ Read  |
| `schoolsManagement`                  |     ✅ CRUD      |  ✅ Read  |
| `organizationsManagement`            |     ✅ CRUD      |  ✅ Read  |
| **Academic Calendar**                |                  |           |
| `academicSessionsManagement`         |     ✅ CRUD      |  ✅ Read  |
| `termsManagement`                    |     ✅ Read      |  ✅ Read  |
| `gradingPeriodsManagement`           |     ✅ CRUD      |  ✅ Read  |
| **Demographics**                     |                  |           |
| `demographicsManagement`             |     ✅ CRUD      |    ❌     |
| **Gradebook**                        |                  |           |
| `lineItemsManagement`                |     ✅ CRUD      |    ❌     |
| `resultsManagement`                  |     ✅ CRUD      |    ❌     |
| `categoriesManagement`               |     ✅ CRUD      |    ❌     |
| `scoreScalesManagement`              |     ✅ CRUD      |    ❌     |
| **Resources**                        |                  |           |
| `resourcesManagement`                |     ✅ CRUD      |    ❌     |
| `courseComponentsManagement`         |     ✅ CRUD      |    ❌     |
| `courseComponentResourcesManagement` |     ✅ CRUD      |    ❌     |
| **Assessments**                      |                  |           |
| `assessmentLineItemsManagement`      |     ✅ CRUD      |    ❌     |
| `assessmentResultsManagement`        |     ✅ CRUD      |    ❌     |

**Legend:**

- ✅ CRUD = Create, Read, Update, Delete all supported
- ✅ Read = Read-only access (GET operations)
- ❌ = Not supported by this provider

> **Note:** This table reflects the typical capabilities of each provider. Actual availability may vary based on your specific contract, configuration, or API version. Always consult your provider's documentation for the most accurate information.

### Nested Queries

Many modules support nested queries to fetch related data efficiently:

```typescript
// Get all students in a specific class
const students = await client.classesManagement.getStudentsForClass({
    classSourcedId: 'class-123',
});

// Get all classes for a specific school
const classes = await client.schoolsManagement.getClassesForSchool({
    schoolSourcedId: 'school-456',
});

// Get all enrollments for a class
const enrollments = await client.classesManagement.getEnrollmentsForClass({
    classSourcedId: 'class-123',
});
```

Common nested query patterns:

- `schools/{id}/classes`, `schools/{id}/students`, `schools/{id}/teachers`
- `classes/{id}/students`, `classes/{id}/teachers`, `classes/{id}/enrollments`
- `users/{id}/classes`, `students/{id}/classes`, `teachers/{id}/classes`
- `terms/{id}/classes`, `courses/{id}/classes`

## OneRoster Versions

The SDK supports both OneRoster v1.1 and v1.2 specifications. The default is v1.2.

### Version 1.2 (Default)

OneRoster v1.2 uses OAuth 2.0 authentication and the `/ims/oneroster/rostering/v1p2/` path format:

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-server.com',
    // oneRosterVersion defaults to '1.2'
    security: {
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
        tokenURL: 'https://your-server.com/oauth/token',
    },
});
```

### Version 1.1

OneRoster v1.1 uses OAuth 1.0a authentication and the `/ims/oneroster/v1p1/` path format:

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-server.com',
    oneRosterVersion: '1.1',
    security: {
        authType: 'oauth1',
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
    },
});
```

> **Note:** OAuth 1.0a is only supported in OneRoster v1.1. Per the 1EdTech specification, v1.2 requires OAuth 2.0.

## Authentication

### OAuth 2.0 (Default for v1.2)

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-server.com',
    security: {
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
        tokenURL: 'https://your-server.com/oauth/token',
    },
});
```

You can also use environment variables:

- `ONEROSTER_CLIENT_ID`
- `ONEROSTER_CLIENT_SECRET`
- `ONEROSTER_TOKEN_URL`

### OAuth 1.0a (v1.1 only)

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-server.com',
    oneRosterVersion: '1.1',
    security: {
        authType: 'oauth1',
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
    },
});
```

### Bearer Token

If you already have a token:

```typescript
import { OneRoster } from '@eduware/oneroster';

const client = new OneRoster({
    serverURL: 'https://your-server.com',
    security: {
        authType: 'bearer',
        bearerToken: 'your-access-token',
    },
});
```

## Pagination

All list endpoints support automatic pagination using async iterables:

```typescript
const result = await client.usersManagement.getAllUsers({
    limit: 100, // Items per page (default: 100)
    offset: 0, // Starting offset
});

// Iterate through all pages automatically
for await (const page of result) {
    for (const user of page.users ?? []) {
        console.log(user.givenName, user.familyName);
    }
}
```

### Total Count

The OneRoster API returns the total number of records in the `X-Total-Count` HTTP header. This value is automatically extracted and exposed on each page result:

```typescript
const result = await client.usersManagement.getAllUsers({});

// Access total count from the first page
if (result.ok) {
    console.log(`Total users: ${result.value.totalCount}`);
}

// Or during iteration
for await (const page of result) {
    if (page.ok) {
        console.log(`Page has ${page.value.users?.length ?? 0} users`);
        console.log(`Total: ${page.totalCount}`);
    }
}
```

> **Note:** The `totalCount` property is `undefined` if the server does not return the `X-Total-Count` header.

## Filtering & Sorting

```typescript
// Filter by status
const activeUsers = await client.usersManagement.getAllUsers({
    filter: "status='active'",
});

// Multiple filters
const filteredUsers = await client.usersManagement.getAllUsers({
    filter: "status='active' AND role='student'",
});

// Sorting
const sortedUsers = await client.usersManagement.getAllUsers({
    sort: 'familyName',
    orderBy: 'asc',
});

// Select specific fields
const partialUsers = await client.usersManagement.getAllUsers({
    fields: 'sourcedId,givenName,familyName,email',
});
```

## Error Handling

```typescript
import { OneRoster } from '@eduware/oneroster';
import * as errors from '@eduware/oneroster/models/errors';

const client = new OneRoster({
    /* ... */
});

try {
    const user = await client.usersManagement.getUser({
        sourcedId: 'non-existent-id',
    });
} catch (error) {
    if (error instanceof errors.NotFoundResponseError) {
        console.log('User not found');
    } else if (error instanceof errors.UnauthorizedRequestResponseError) {
        console.log('Authentication failed');
    } else if (error instanceof errors.OneRosterError) {
        // Base class for all HTTP errors
        console.log('HTTP Error:', error.statusCode, error.message);
    }
}
```

### Error Classes

| Error                              | Status Code | Description                |
| ---------------------------------- | ----------- | -------------------------- |
| `BadRequestResponseError`          | 400         | Invalid request parameters |
| `UnauthorizedRequestResponseError` | 401         | Authentication failed      |
| `ForbiddenResponseError`           | 403         | Access denied              |
| `NotFoundResponseError`            | 404         | Resource not found         |
| `UnprocessableEntityResponseError` | 422         | Validation error           |
| `TooManyRequestsResponseError`     | 429         | Rate limit exceeded        |
| `InternalServerErrorResponse`      | 500         | Server error               |

## Standalone Functions

For optimal bundle sizes in browser and serverless environments, use standalone functions:

```typescript
import { OneRosterCore } from '@eduware/oneroster/core.js';
import { usersManagementGetAllUsers } from '@eduware/oneroster/funcs/usersManagementGetAllUsers.js';

const client = new OneRosterCore({
    security: {
        clientID: process.env.ONEROSTER_CLIENT_ID!,
        clientSecret: process.env.ONEROSTER_CLIENT_SECRET!,
    },
});

const result = await usersManagementGetAllUsers(client, {});

if (result.ok) {
    for await (const page of result.value) {
        console.log(page.users);
    }
} else {
    console.error('Error:', result.error);
}
```

See [FUNCTIONS.md](./FUNCTIONS.md) for the complete list of standalone functions.

## MCP Server

This SDK includes an MCP (Model Context Protocol) server for AI applications. The MCP server exposes OneRoster API operations as tools that can be used by AI assistants.

### Basic Configuration (OAuth 2.0)

```json
{
    "mcpServers": {
        "OneRoster": {
            "command": "npx",
            "args": [
                "-y",
                "--package",
                "@eduware/oneroster",
                "--",
                "mcp",
                "start",
                "--server-url",
                "https://your-oneroster-server.com",
                "--client-id",
                "your-client-id",
                "--client-secret",
                "your-client-secret",
                "--token-url",
                "https://your-server.com/oauth/token"
            ]
        }
    }
}
```

### ClassLink Configuration (Bearer Token)

For ClassLink's proxy mode with pre-authenticated bearer tokens:

```json
{
    "mcpServers": {
        "OneRoster": {
            "command": "npx",
            "args": [
                "-y",
                "--package",
                "@eduware/oneroster",
                "--",
                "mcp",
                "start",
                "--server-url",
                "https://oneroster-proxy.apis.classlink.com/YOUR_TENANT_ID",
                "--auth-type",
                "bearer",
                "--bearer-token",
                "your-bearer-token",
                "--client-profile",
                "classlink"
            ]
        }
    }
}
```

### OAuth 1.0a Configuration (OneRoster v1.1)

For OneRoster v1.1 servers using OAuth 1.0a:

```json
{
    "mcpServers": {
        "OneRoster": {
            "command": "npx",
            "args": [
                "-y",
                "--package",
                "@eduware/oneroster",
                "--",
                "mcp",
                "start",
                "--server-url",
                "https://your-oneroster-server.com",
                "--auth-type",
                "oauth1",
                "--oneroster-version",
                "1.1",
                "--client-id",
                "your-client-id",
                "--client-secret",
                "your-client-secret"
            ]
        }
    }
}
```

### Command Line Arguments

| Argument              | Description                                                   | Default   | Required          |
| --------------------- | ------------------------------------------------------------- | --------- | ----------------- |
| `--server-url`        | OneRoster API server URL                                      | —         | Yes               |
| `--auth-type`         | Authentication type: `oauth1`, `oauth2`, or `bearer`          | `oauth2`  | No                |
| `--client-id`         | OAuth client ID                                               | —         | For oauth1/oauth2 |
| `--client-secret`     | OAuth client secret                                           | —         | For oauth1/oauth2 |
| `--token-url`         | OAuth 2.0 token endpoint URL                                  | —         | For oauth2        |
| `--bearer-token`      | Pre-authenticated bearer token                                | —         | For bearer        |
| `--client-profile`    | Client profile: `full` (all tools) or `classlink` (read-only) | `full`    | No                |
| `--oneroster-version` | OneRoster API version: `1.1` or `1.2`                         | `1.2`     | No                |
| `--transport`         | Transport protocol: `stdio` or `sse`                          | `stdio`   | No                |
| `--port`              | Port for SSE transport                                        | `2718`    | No                |
| `--tool`              | Specific tools to mount (repeatable)                          | All tools | No                |
| `--log-level`         | Log level: `debug`, `info`, `warn`, `error`                   | `info`    | No                |
| `--env`               | Environment variables (format: `KEY=value`, repeatable)       | —         | No                |

### Client Profiles

The `--client-profile` flag restricts which tools are available:

- **`full`** (default): All OneRoster tools including create, update, and delete operations
- **`classlink`**: Read-only tools matching ClassLink's supported endpoints

Using the appropriate profile prevents AI assistants from attempting unsupported operations.

## Runtime Support

- **Browsers:** Chrome, Safari, Edge, Firefox (evergreen)
- **Node.js:** v18+ (LTS)
- **Bun:** v1+
- **Deno:** v1.39+

See [RUNTIMES.md](./RUNTIMES.md) for detailed runtime information.

## API Reference

For the complete list of available methods, see [FUNCTIONS.md](./FUNCTIONS.md).

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.

## License

MIT
