add skill for the ms graph sdk, which is a wrapper (#1701)

This commit is contained in:
Lovy Jain
2026-05-14 06:39:26 +05:30
committed by GitHub
parent b010c5ebfa
commit 8bd33a94af
5 changed files with 906 additions and 0 deletions
+212
View File
@@ -0,0 +1,212 @@
# Microsoft Graph SDK for .NET
Use this reference when the target project is written in C# or another .NET language.
## Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-dotnet>
- Samples: <https://github.com/microsoftgraph/msgraph-training-dotnet>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/main/CHANGELOG.md>
## Packages
```xml
<!-- Microsoft Graph SDK v5 (current) -->
<PackageReference Include="Microsoft.Graph" Version="5.*" />
<!-- Azure Identity for credential providers -->
<PackageReference Include="Azure.Identity" Version="1.*" />
```
Install via CLI:
```bash
dotnet add package Microsoft.Graph
dotnet add package Azure.Identity
```
## Client setup
### Managed Identity (Azure-hosted apps — preferred)
```csharp
using Azure.Identity;
using Microsoft.Graph;
var credential = new DefaultAzureCredential();
var graphClient = new GraphServiceClient(credential);
```
### Client credentials (app-only / daemon)
```csharp
var credential = new ClientSecretCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
clientId: Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"),
clientSecret: Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET")
);
var graphClient = new GraphServiceClient(credential);
```
Prefer `ClientCertificateCredential` over `ClientSecretCredential` in production.
### On-Behalf-Of (OBO) — agent / API acting as the signed-in user
```csharp
// incomingToken is the bearer token received from the caller
var credential = new OnBehalfOfCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
clientId: Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"),
clientSecret: Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET"),
userAssertion: new UserAssertion(incomingToken)
);
var graphClient = new GraphServiceClient(credential);
```
### Interactive (local dev / CLI)
```csharp
var credential = new InteractiveBrowserCredential();
var graphClient = new GraphServiceClient(credential);
```
## Common call patterns
### Get a resource with field selection
```csharp
var user = await graphClient.Me.GetAsync(config =>
{
config.QueryParameters.Select = ["displayName", "mail", "jobTitle"];
});
```
### List with filter and select
```csharp
var messages = await graphClient.Me.Messages.GetAsync(config =>
{
config.QueryParameters.Filter = "isRead eq false";
config.QueryParameters.Select = ["subject", "from", "receivedDateTime"];
config.QueryParameters.Top = 25;
config.QueryParameters.Orderby = ["receivedDateTime desc"];
});
```
### Pagination with PageIterator
```csharp
var messages = await graphClient.Me.Messages.GetAsync();
var allMessages = new List<Message>();
var pageIterator = PageIterator<Message, MessageCollectionResponse>
.CreatePageIterator(graphClient, messages, (msg) =>
{
allMessages.Add(msg);
return true; // return false to stop early
});
await pageIterator.IterateAsync();
```
### Send an email
```csharp
await graphClient.Me.SendMail.PostAsync(new SendMailPostRequestBody
{
Message = new Message
{
Subject = "Hello from Graph",
Body = new ItemBody { ContentType = BodyType.Text, Content = "Test message" },
ToRecipients = [new Recipient { EmailAddress = new EmailAddress { Address = "user@contoso.com" } }]
}
});
```
### Post a Teams channel message
```csharp
await graphClient.Teams[teamId].Channels[channelId].Messages.PostAsync(new ChatMessage
{
Body = new ItemBody { ContentType = BodyType.Html, Content = "<b>Hello from Graph!</b>" }
});
```
## Batch requests
```csharp
using Microsoft.Graph.Models;
var batchRequestContent = new BatchRequestContentCollection(graphClient);
var meRequest = await batchRequestContent.AddBatchRequestStepAsync(
graphClient.Me.ToGetRequestInformation());
var messagesRequest = await batchRequestContent.AddBatchRequestStepAsync(
graphClient.Me.Messages.ToGetRequestInformation());
var batchResponse = await graphClient.Batch.PostAsync(batchRequestContent);
var me = await batchResponse.GetResponseByIdAsync<User>(meRequest);
var msgs = await batchResponse.GetResponseByIdAsync<MessageCollectionResponse>(messagesRequest);
```
## Delta queries
```csharp
// First sync — get all + deltaLink
var deltaResponse = await graphClient.Users.Delta.GetAsDeltaGetResponseAsync();
string? deltaLink = null;
var pageIterator = PageIterator<User, Microsoft.Graph.Users.Delta.DeltaGetResponse>
.CreatePageIterator(graphClient, deltaResponse, (user) => { /* process */ return true; },
(req) => { deltaLink = /* extract from response */; return req; });
await pageIterator.IterateAsync();
// Store deltaLink for next run
// Subsequent sync — only changes
// Use the stored deltaLink directly as the next request URL
```
## Throttling / retry middleware
The SDK includes retry middleware enabled by default. For explicit control:
```csharp
var handlers = GraphClientFactory.CreateDefaultHandlers();
// RetryHandler is included; configure max retries if needed
var httpClient = GraphClientFactory.Create(handlers);
var graphClient = new GraphServiceClient(httpClient, credential);
```
Always check `Retry-After` if building custom retry logic — do not use fixed exponential backoff.
## Dependency injection (ASP.NET Core / .NET Worker)
```csharp
// Program.cs
builder.Services.AddSingleton<GraphServiceClient>(_ =>
{
var credential = new DefaultAzureCredential();
return new GraphServiceClient(credential);
});
```
## .NET-specific guidance
- Target .NET 8+ for new projects.
- Use `async`/`await` throughout — all Graph SDK calls are async.
- Register `GraphServiceClient` as a singleton (it caches tokens internally).
- Use `ILogger` to log Graph exceptions — catch `ODataError` for Graph-specific error details.
- For ASP.NET Core APIs using OBO, inject the incoming token from `IHttpContextAccessor` and construct the credential per-request (not as a singleton).
```csharp
// Catching Graph errors
try
{
var user = await graphClient.Me.GetAsync();
}
catch (ODataError odataError)
{
Console.WriteLine($"Graph error: {odataError.Error?.Code} - {odataError.Error?.Message}");
}
```
+290
View File
@@ -0,0 +1,290 @@
# Microsoft Graph SDK for Python
Use this reference when the target project is written in Python.
## Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-python>
- Samples: <https://github.com/microsoftgraph/msgraph-training-python>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/CHANGELOG.md>
## Packages
```bash
pip install msgraph-sdk azure-identity
```
Or in `requirements.txt` / `pyproject.toml`:
```
msgraph-sdk>=1.0.0
azure-identity>=1.15.0
```
## Client setup
### Managed Identity (Azure-hosted apps — preferred)
```python
from azure.identity.aio import DefaultAzureCredential
from msgraph import GraphServiceClient
credential = DefaultAzureCredential()
graph_client = GraphServiceClient(credential)
```
The Python SDK is async-first (`asyncio`). Use `azure.identity.aio` (async variants), not `azure.identity`.
### Client credentials (app-only / daemon)
```python
import os
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient
credential = ClientSecretCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
)
graph_client = GraphServiceClient(credential)
```
Prefer `CertificateCredential` over `ClientSecretCredential` in production.
### On-Behalf-Of (OBO) — agent / API acting as the signed-in user
```python
from azure.identity.aio import OnBehalfOfCredential
# incoming_token is the bearer token from the caller
credential = OnBehalfOfCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
user_assertion=incoming_token,
)
graph_client = GraphServiceClient(credential)
```
Construct a new `GraphServiceClient` per request for OBO — the credential is user-scoped.
### Device code (CLI / local dev)
```python
from azure.identity.aio import DeviceCodeCredential
credential = DeviceCodeCredential(
client_id=os.environ["AZURE_CLIENT_ID"],
tenant_id=os.environ["AZURE_TENANT_ID"],
)
graph_client = GraphServiceClient(credential, scopes=["User.Read", "Mail.Read"])
```
## Common call patterns
All Graph SDK calls in Python are async. Always run inside an async context.
### Get a resource with field selection
```python
import asyncio
from msgraph.generated.me.me_request_builder import MeRequestBuilder
from kiota_abstractions.base_request_configuration import RequestConfiguration
async def get_my_profile():
query_params = MeRequestBuilder.MeRequestBuilderGetQueryParameters(
select=["displayName", "mail", "jobTitle"]
)
config = RequestConfiguration(query_parameters=query_params)
user = await graph_client.me.get(request_configuration=config)
return user
asyncio.run(get_my_profile())
```
### List messages with filter and select
```python
from msgraph.generated.me.messages.messages_request_builder import MessagesRequestBuilder
async def get_unread_messages():
query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
filter="isRead eq false",
select=["subject", "from", "receivedDateTime"],
top=25,
orderby=["receivedDateTime desc"],
)
config = RequestConfiguration(query_parameters=query_params)
result = await graph_client.me.messages.get(request_configuration=config)
return result
```
### Pagination with PageIterator
```python
from msgraph.generated.models.message import Message
from msgraph.core import PageIterator
async def get_all_messages():
first_page = await graph_client.me.messages.get()
all_messages: list[Message] = []
async def process_message(message: Message) -> bool:
all_messages.append(message)
return True # return False to stop early
page_iterator = PageIterator(
response=first_page,
request_adapter=graph_client.request_adapter,
constructor=Message,
)
await page_iterator.iterate(callback=process_message)
return all_messages
```
### Send an email
```python
from msgraph.generated.models.message import Message
from msgraph.generated.models.item_body import ItemBody
from msgraph.generated.models.body_type import BodyType
from msgraph.generated.models.recipient import Recipient
from msgraph.generated.models.email_address import EmailAddress
from msgraph.generated.me.send_mail.send_mail_post_request_body import SendMailPostRequestBody
async def send_email():
body = SendMailPostRequestBody(
message=Message(
subject="Hello from Graph",
body=ItemBody(content_type=BodyType.Text, content="Test message"),
to_recipients=[
Recipient(email_address=EmailAddress(address="user@contoso.com"))
],
)
)
await graph_client.me.send_mail.post(body)
```
### Post a Teams channel message
```python
from msgraph.generated.models.chat_message import ChatMessage
from msgraph.generated.models.item_body import ItemBody
from msgraph.generated.models.body_type import BodyType
async def post_channel_message(team_id: str, channel_id: str):
message = ChatMessage(
body=ItemBody(content_type=BodyType.Html, content="<b>Hello from Graph!</b>")
)
await graph_client.teams.by_team_id(team_id).channels.by_channel_id(channel_id).messages.post(message)
```
## Batch requests
```python
from kiota_http.middleware.options import ResponseHandlerOption
import json
async def batch_example():
batch_body = {
"requests": [
{"id": "1", "method": "GET", "url": "/me"},
{"id": "2", "method": "GET", "url": "/me/messages?$top=5&$select=subject"},
]
}
# Use the raw HTTP client for batch
response = await graph_client.request_adapter.send_primitive_async(
# Alternatively, use the requests library with a token from the credential
)
```
For batch in Python, it's often simpler to use `httpx` with an acquired token when the batch helper is not yet fully supported:
```python
import httpx
from azure.identity.aio import ClientSecretCredential
async def batch_with_httpx(credential):
token = await credential.get_token("https://graph.microsoft.com/.default")
async with httpx.AsyncClient() as client:
response = await client.post(
"https://graph.microsoft.com/v1.0/$batch",
headers={"Authorization": f"Bearer {token.token}"},
json={
"requests": [
{"id": "1", "method": "GET", "url": "/me"},
{"id": "2", "method": "GET", "url": "/me/messages?$top=5"},
]
},
)
return response.json()
```
## Delta queries
```python
async def delta_sync(stored_delta_link: str | None = None):
if stored_delta_link:
# Use delta link directly
response = await graph_client.request_adapter.send_async(...)
else:
response = await graph_client.users.delta.get()
users = []
async def collect(user):
users.append(user)
return True
page_iterator = PageIterator(response=response, request_adapter=graph_client.request_adapter, constructor=...)
await page_iterator.iterate(callback=collect)
delta_link = page_iterator.delta_link # store this for next run
return users, delta_link
```
## Throttling / retry
The SDK's HTTP transport handles 429 retry automatically when using the default `GraphClientFactory`. For explicit control:
```python
import asyncio
import httpx
async def call_with_retry(graph_client, call_fn, max_retries=5):
for attempt in range(max_retries):
try:
return await call_fn()
except Exception as e:
if "429" in str(e):
retry_after = int(getattr(e, "retry_after", 10))
await asyncio.sleep(retry_after)
else:
raise
```
## Python-specific guidance
- The Python Graph SDK is **async-first** — use `asyncio.run()` or an async framework (FastAPI, aiohttp).
- Always use `azure.identity.aio` (not `azure.identity`) for async contexts.
- Close credentials when done: `await credential.close()` or use as async context managers.
- Python SDK model classes use `snake_case` for properties (Graph JSON uses `camelCase` — the SDK maps automatically).
- Use `asyncio.gather()` for concurrent but independent Graph calls (mind throttling limits).
- For FastAPI: use lifespan events to init `GraphServiceClient` once and close the credential on shutdown.
```python
# FastAPI integration example
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
credential = DefaultAzureCredential()
app.state.graph_client = GraphServiceClient(credential)
yield
await credential.close()
app = FastAPI(lifespan=lifespan)
```
+257
View File
@@ -0,0 +1,257 @@
# Microsoft Graph SDK for TypeScript / JavaScript
Use this reference when the target project uses TypeScript or JavaScript (Node.js or browser).
## Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-javascript>
- Samples: <https://github.com/microsoftgraph/msgraph-training-typescript>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/main/CHANGELOG.md>
## Packages
```bash
npm install @microsoft/microsoft-graph-client @azure/identity
npm install -D @microsoft/microsoft-graph-types # TypeScript type definitions
```
For Node.js environments, also install the fetch polyfill:
```bash
npm install node-fetch
```
## Client setup
### Managed Identity (Azure-hosted apps — preferred)
```typescript
import { Client } from "@microsoft/microsoft-graph-client";
import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials/index.js";
import { DefaultAzureCredential } from "@azure/identity";
const credential = new DefaultAzureCredential();
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });
```
### Client credentials (app-only / daemon)
```typescript
import { ClientSecretCredential } from "@azure/identity";
const credential = new ClientSecretCredential(
process.env.AZURE_TENANT_ID!,
process.env.AZURE_CLIENT_ID!,
process.env.AZURE_CLIENT_SECRET!
);
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });
```
### On-Behalf-Of (OBO) — agent / API acting as the signed-in user
```typescript
import { OnBehalfOfCredential } from "@azure/identity";
// incomingToken is the bearer token received from the caller (e.g. from req.headers.authorization)
const credential = new OnBehalfOfCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
clientSecret: process.env.AZURE_CLIENT_SECRET!,
userAssertionToken: incomingToken,
});
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });
```
For OBO, create a new client per request (credential is user-scoped, not singleton-safe).
### Interactive (local dev / CLI — Node.js)
Use `InteractiveBrowserCredential` when a browser is available. Use `DeviceCodeCredential` for headless environments (SSH, CI-adjacent, WSL):
```typescript
import { InteractiveBrowserCredential, DeviceCodeCredential } from "@azure/identity";
// Opens a browser tab — requires redirect URI http://localhost in app registration
const credential = new InteractiveBrowserCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
});
// Prints a device code to the terminal — works in any environment
const credential = new DeviceCodeCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
userPromptCallback: (info) => console.log(info.message),
});
```
Both require the app registration platform to be **"Mobile and desktop applications"**. Neither uses a client secret.
## Common call patterns
### Get a resource with field selection
```typescript
import { User } from "@microsoft/microsoft-graph-types";
const user: User = await graphClient
.api("/me")
.select("displayName,mail,jobTitle")
.get();
```
### List with filter, select, and ordering
```typescript
const result = await graphClient
.api("/me/messages")
.filter("isRead eq false")
.select("subject,from,receivedDateTime")
.top(25)
.orderby("receivedDateTime desc")
.get();
```
### Pagination with PageIterator
```typescript
import { PageIterator } from "@microsoft/microsoft-graph-client";
import { Message } from "@microsoft/microsoft-graph-types";
const firstPage = await graphClient.api("/me/messages").top(25).get();
const allMessages: Message[] = [];
const pageIterator = new PageIterator(
graphClient,
firstPage,
(message: Message) => {
allMessages.push(message);
return true; // return false to stop early
}
);
await pageIterator.iterate();
```
### Send an email
```typescript
await graphClient.api("/me/sendMail").post({
message: {
subject: "Hello from Graph",
body: { contentType: "Text", content: "Test message" },
toRecipients: [{ emailAddress: { address: "user@contoso.com" } }],
},
});
```
### Post a Teams channel message
```typescript
await graphClient.api(`/teams/${teamId}/channels/${channelId}/messages`).post({
body: { contentType: "html", content: "<b>Hello from Graph!</b>" },
});
```
### Upload a file to OneDrive (small files ≤ 4 MB)
```typescript
const content = Buffer.from("file contents");
await graphClient
.api(`/me/drive/root:/${fileName}:/content`)
.putStream(content);
```
For files > 4 MB, use an upload session (`createUploadSession`).
## Batch requests
```typescript
const batchRequestBody = {
requests: [
{ id: "1", method: "GET", url: "/me" },
{ id: "2", method: "GET", url: "/me/messages?$top=5&$select=subject" },
],
};
const batchResponse = await graphClient.api("/$batch").post(batchRequestBody);
const meResponse = batchResponse.responses.find((r: any) => r.id === "1");
const messagesResponse = batchResponse.responses.find((r: any) => r.id === "2");
```
## Delta queries
```typescript
// First sync
let response = await graphClient.api("/users/delta").get();
const users: any[] = [];
while (response["@odata.nextLink"]) {
users.push(...response.value);
response = await graphClient.api(response["@odata.nextLink"]).get();
}
users.push(...response.value);
const deltaLink: string = response["@odata.deltaLink"];
// Store deltaLink durably for next sync run
// Next sync — only changes
const changesResponse = await graphClient.api(deltaLink).get();
```
## Throttling / retry middleware
The SDK includes retry middleware by default. For explicit configuration:
```typescript
import {
Client,
RetryHandlerOptions,
RetryHandler,
MiddlewareFactory,
} from "@microsoft/microsoft-graph-client";
const retryOptions = new RetryHandlerOptions({ maxRetries: 5 });
const middleware = MiddlewareFactory.getDefaultMiddlewareChain(authProvider);
const graphClient = Client.initWithMiddleware({ middleware });
```
Always honour the `Retry-After` header value — do not use fixed backoff when Graph specifies a wait time.
## TypeScript-specific guidance
- Import types from `@microsoft/microsoft-graph-types` for full IntelliSense on Graph resources.
- The `.api()` chain returns `any` — cast to the appropriate type from `@microsoft/microsoft-graph-types`.
- For ESM projects, use the `/index.js` path suffix on deep imports (e.g., `azureTokenCredentials/index.js`).
- Use `async`/`await` consistently — all Graph calls return Promises.
- Singleton the `graphClient` in application-level code (e.g., Express app init); for OBO flows, construct per-request.
- In Node.js 18+, `fetch` is available natively — no polyfill needed.
```typescript
// Type-safe response example
import { MessageCollectionResponse } from "@microsoft/microsoft-graph-types";
const response: MessageCollectionResponse = await graphClient
.api("/me/messages")
.select("subject,from")
.get();
const messages = response.value ?? [];
```