add-office365-users
Skill4 starsChanged 8 days agoMITView on GitHub
Adds Office 365 Users by delegating to `/add-data-source` with `api-id=shared_office365users` and action mode. Use for user profiles, managers, direct reports, people search, and profile photos.
In this repository38 other files it gives its agents.
- github-actions/AGENTS.mdAGENTS.md
- plugins/copilot-managed-runtime/AGENTS.mdAGENTS.md
- samples/CodeFlow/AGENTS.mdAGENTS.md
- samples/LearningHub/AGENTS.mdAGENTS.md
- samples/TaskManager/AGENTS.mdAGENTS.md
- add-azuredevopsSkill
- add-backendSkill
- add-data-sourceSkill
- add-dataverseSkill
- add-excelSkill
- add-mcscopilotSkill
- add-office365Skill
- add-onedriveSkill
- add-sharepointSkill
- add-teamsSkill
- add-workiqSkill
- create-appSkill
- delete-appSkill
- deploySkill
- devSkill
- list-appsSkill
- list-connectorsSkill
- playSkill
- share-linkSkill
- shareSkill
- stateSkill
- add-data-sourceSkill
- setup-codeflowSkill
- add-data-sourceSkill
- setup-codeflowSkill
- add-data-sourceSkill
- setup-learninghubSkill
- add-data-sourceSkill
- setup-learninghubSkill
- add-data-sourceSkill
- setup-taskmanagerSkill
- add-data-sourceSkill
- setup-taskmanagerSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 24
After the connector is added, import the generated service:
- Distinctive: line 30
Use the V2 methods for profiles and reporting relationships:
- Very specific: line 91
Call UserPhotoMetadata before downloading the photo.
- Very specific: line 168
Always fetch photos through the generated connector service.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
📋 Shared Instructions: shared-instructions.md — Cross-cutting concerns.
Add Office 365 Users (Wrapper)
This skill is a thin wrapper. Use /add-data-source as the single implementation path.
Delegation contract
Invoke /add-data-source with:
api-id:shared_office365usersmode:action
Office 365 Users: Method Selection Guide
After the connector is added, import the generated service:
import { Office365UsersService } from '../../generated/services/Office365UsersService'Use the V2 methods for profiles and reporting relationships:
| Scenario | Method | Result |
| Current signed-in user | MyProfile_V2($select?) | One GraphUser_V1 |
| Specific user by ID or UPN | UserProfile_V2(id, $select?) | One GraphUser_V1 |
| User's manager | Manager_V2(id, $select?) | One GraphUser_V1 |
| Direct reports | DirectReports_V2(id, $select?, $top?) | Profiles in result.data.value |
| Search people | SearchUserV2(searchTerm, top?, ...) | Search response in result.data.value |
| Profile photo | UserPhotoMetadata(userId), then UserPhoto_V2(id) | Metadata, then binary image data |
DirectReports_V2 requires the target manager's Microsoft Entra directory ID or user
principal name (UPN). For the signed-in user, call
MyProfile_V2('id,userPrincipalName'), then pass the returned id or fall back to
userPrincipalName. For a known person, pass their directory ID or UPN directly.
Profile and direct-report pattern
Request only the fields the UI needs:
const fields = [
'id',
'displayName',
'jobTitle',
'department',
'mail',
'userPrincipalName',
'officeLocation',
].join(',')
const myProfile = await Office365UsersService.MyProfile_V2(
'id,userPrincipalName',
)
if (!myProfile.success) {
throw new Error(myProfile.error?.message ?? 'Failed to resolve current user')
}
const managerId =
myProfile.data?.id ?? myProfile.data?.userPrincipalName
if (!managerId) {
throw new Error('Current user profile did not include an ID or UPN')
}
const result = await Office365UsersService.DirectReports_V2(
managerId,
fields,
)
if (!result.success) {
throw new Error(result.error?.message ?? 'Failed to load direct reports')
}
const people = result.data?.value ?? []An empty value array is valid and means the user has no direct reports.
Profile photos: binary response and CSP-safe rendering
Call UserPhotoMetadata before downloading the photo. HasPhoto: false is a valid
no-photo state; render initials or another accessible fallback.
Image responses are a special runtime contract: the generated UserPhoto_V2 signature may
declare IOperationResult<string>, but the Copilot Managed Runtime SDK decodes an image/* response into
a Uint8Array. Do not interpolate that byte array directly into a base64 URL, and do not use a
blob: URL—the deployed App Player content security policy may block blob: under img-src.
Convert the bytes to a data: URL:
function bytesToBase64(bytes: Uint8Array) {
const chunkSize = 0x8000
let binary = ''
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
binary += String.fromCharCode(
...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length)),
)
}
return btoa(binary)
}
function toImageDataUrl(value: unknown, contentType = 'image/jpeg') {
if (value instanceof Uint8Array) {
return `data:${contentType};base64,${bytesToBase64(value)}`
}
if (value instanceof ArrayBuffer) {
return `data:${contentType};base64,${bytesToBase64(new Uint8Array(value))}`
}
if (typeof value === 'string') {
if (/^data:image\/[a-z0-9.+-]+;base64,/i.test(value)) {
return value
}
if (/^https?:/i.test(value)) {
throw new Error(
'Profile photo returned an HTTP URL instead of binary image data.',
)
}
return `data:${contentType};base64,${value}`
}
throw new Error('The profile photo response used an unsupported format.')
}UserPhoto_V2 has a binary response contract and does not return an external image URL.
An HTTP value indicates an unexpected response shape, so the helper rejects it rather than
passing a potentially CSP-blocked or authenticated URL to <img src>.
Complete photo flow:
const metadata = await Office365UsersService.UserPhotoMetadata(userId)
if (!metadata.success) {
throw new Error(metadata.error?.message ?? 'Failed to check profile photo')
}
if (!metadata.data?.HasPhoto) {
return undefined
}
const photo = await Office365UsersService.UserPhoto_V2(userId)
if (!photo.success) {
throw new Error(photo.error?.message ?? 'Failed to load profile photo')
}
return photo.data
? toImageDataUrl(photo.data, metadata.data.ContentType)
: undefinedAlways fetch photos through the generated connector service. Do not call Microsoft Graph
directly and do not assign the authenticated connector runtime URL to <img src>.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

