microsoft-teams-graph 2.0.0a9__tar.gz

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.
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+
7
+ # Environments
8
+ .env
9
+ .venv
10
+ env/
11
+ venv/
12
+ ENV/
13
+ env.bak/
14
+ venv.bak/
15
+
16
+ # mypy
17
+ .mypy_cache/
18
+ .dmypy.json
19
+ dmypy.json
20
+
21
+ .copilot-instructions.md
22
+
23
+ # other
24
+ .DS_STORE
25
+ *.bak
26
+ *~
27
+ *.tmp
28
+
29
+ ref/
30
+ py.typed
31
+ CLAUDE.md
32
+
33
+ .env.claude/
34
+ .claude/
35
+
36
+ examples/**/.vscode/
37
+ examples/**/appPackage/
38
+ examples/**/infra/
39
+ examples/**/teamsapp*
40
+ examples/**/aad.manifest.json
41
+
42
+ # Node (from tab)
43
+ node_modules
44
+ dist/
45
+ build/
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-teams-graph
3
+ Version: 2.0.0a9
4
+ Summary: The Graph package for a Microsoft Teams agent
5
+ Author-email: Microsoft <TeamsAISDKFeedback@microsoft.com>
6
+ License: MIT
7
+ Keywords: agents,ai,bot,graph,microsoft,teams
8
+ Requires-Python: <3.15,>=3.12
9
+ Requires-Dist: azure-core>=1.31.0
10
+ Requires-Dist: microsoft-teams-common
11
+ Requires-Dist: msgraph-sdk<2.0.0,>=1.30.0
12
+ Requires-Dist: pyjwt[crypto]>=2.10.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Microsoft Teams Graph Integration
16
+
17
+ <p>
18
+ <a href="https://pypi.org/project/microsoft-teams-graph" target="_blank">
19
+ <img src="https://img.shields.io/pypi/v/microsoft-teams-graph" />
20
+ </a>
21
+ <a href="https://pypi.org/project/microsoft-teams-graph" target="_blank">
22
+ <img src="https://img.shields.io/pypi/dw/microsoft-teams-graph" />
23
+ </a>
24
+ </p>
25
+
26
+ This package provides seamless access to Microsoft Graph APIs from Teams bots and agents built with the Microsoft Teams SDK for Python.
27
+
28
+ <a href="https://microsoft.github.io/teams-sdk" target="_blank">
29
+ <img src="https://img.shields.io/badge/📖 Getting Started-blue?style=for-the-badge" />
30
+ </a>
31
+
32
+ ## Key Features
33
+
34
+ - **Token Integration**: Unified token handling using the Token type from microsoft-teams-common
35
+ - **Flexible Token Sources**: Supports strings, StringLike objects, callables, async callables, or None
36
+ - **Automatic Token Resolution**: Leverages common resolve_token utility for consistent handling
37
+
38
+ ## Requirements
39
+
40
+ - Teams SDK for Python
41
+ - Microsoft Graph SDK for Python (msgraph-sdk)
42
+ - Azure Core library (azure-core)
43
+ - Microsoft Teams Common library (microsoft-teams-common)
44
+
45
+ ## Quick Start
46
+
47
+ ### Basic Usage with Teams Bot
48
+
49
+ ```python
50
+ from microsoft_teams.graph import get_graph_client
51
+ from microsoft_teams.apps import App, ActivityContext
52
+ from microsoft_teams.api import MessageActivity
53
+
54
+ app = App()
55
+
56
+ @app.on_message
57
+ async def handle_message(ctx: ActivityContext[MessageActivity]):
58
+ if not ctx.is_signed_in:
59
+ await ctx.sign_in()
60
+ return
61
+
62
+ # Create Graph client using user's token
63
+ graph = get_graph_client(ctx.user_token)
64
+
65
+ # Get user profile
66
+ me = await graph.me.get()
67
+ await ctx.send(f"Hello {me.display_name}!")
68
+
69
+ # Get user's Teams
70
+ teams = await graph.me.joined_teams.get()
71
+ if teams and teams.value:
72
+ team_names = [team.display_name for team in teams.value]
73
+ await ctx.send(f"You're in {len(team_names)} teams: {', '.join(team_names)}")
74
+ ```
75
+
76
+ ### Token Integration
77
+
78
+ ```python
79
+ from microsoft_teams.common.http.client_token import Token
80
+
81
+ def create_token_callable(ctx: ActivityContext) -> Token:
82
+ """Create a callable token that refreshes automatically."""
83
+ def get_fresh_token():
84
+ # This is called on each Graph API request
85
+ return ctx.user_token # Always returns current valid token
86
+
87
+ return get_fresh_token
88
+
89
+ # Use with Graph client
90
+ graph = get_graph_client(create_token_callable(ctx))
91
+ ```
92
+
93
+ await ctx.sign_in()
94
+ return
95
+
96
+ # Use the user token that's already available in the context
97
+ graph = get_graph_client(ctx.user_token)
98
+
99
+ # Make Graph API calls
100
+ me = await graph.me.get()
101
+ await ctx.send(f"Hello {me.display_name}!")
102
+
103
+ # Make Graph API calls
104
+ me = await graph.me.get()
105
+ await ctx.send(f"Hello {me.display_name}!")
106
+
107
+ ````
108
+
109
+ ## Token Type Usage
110
+
111
+ The package uses the Token type from microsoft-teams-common for flexible token handling. You can provide tokens in several formats:
112
+
113
+ ### String Token (Simplest)
114
+
115
+ ```python
116
+ # Direct string token
117
+ graph = get_graph_client("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...")
118
+ ````
119
+
120
+ ### Callable Token (Dynamic)
121
+
122
+ ```python
123
+ def get_token():
124
+ """Callable that returns a string token."""
125
+ # Get your access token from wherever (Teams API, cache, etc.)
126
+ return get_access_token_from_somewhere()
127
+
128
+ # Use the callable with get_graph_client
129
+ graph = get_graph_client(get_token)
130
+ ```
131
+
132
+ ### Async Callable Token
133
+
134
+ ```python
135
+ async def get_token_async():
136
+ """Async callable that returns a string token."""
137
+ # Fetch token asynchronously
138
+ token_response = await some_api_call()
139
+ return token_response.access_token
140
+
141
+ graph = get_graph_client(get_token_async)
142
+ ```
143
+
144
+ ### Dynamic Token Retrieval
145
+
146
+ ```python
147
+ def get_fresh_token():
148
+ """Callable that fetches a fresh token on each invocation."""
149
+ # This will be called each time the Graph client needs a token
150
+ fresh_token = fetch_latest_token_from_api()
151
+ return fresh_token
152
+
153
+ graph = get_graph_client(get_fresh_token)
154
+ ```
155
+
156
+ ## Authentication
157
+
158
+ The package uses Token-based authentication with automatic resolution through the common library. Teams tokens are pre-authorized through the OAuth connection configured in your Azure Bot registration.
159
+
160
+ ## API Usage Examples
161
+
162
+ ```python
163
+ # Get user profile
164
+ me = await graph.me.get()
165
+
166
+ # Get recent emails with specific fields
167
+ from msgraph.generated.users.item.messages.messages_request_builder import MessagesRequestBuilder
168
+
169
+ query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
170
+ select=["subject", "from", "receivedDateTime"],
171
+ top=5
172
+ )
173
+ request_config = MessagesRequestBuilder.MessagesRequestBuilderGetRequestConfiguration(
174
+ query_parameters=query_params
175
+ )
176
+ messages = await graph.me.messages.get(request_configuration=request_config)
177
+ ```
@@ -0,0 +1,163 @@
1
+ # Microsoft Teams Graph Integration
2
+
3
+ <p>
4
+ <a href="https://pypi.org/project/microsoft-teams-graph" target="_blank">
5
+ <img src="https://img.shields.io/pypi/v/microsoft-teams-graph" />
6
+ </a>
7
+ <a href="https://pypi.org/project/microsoft-teams-graph" target="_blank">
8
+ <img src="https://img.shields.io/pypi/dw/microsoft-teams-graph" />
9
+ </a>
10
+ </p>
11
+
12
+ This package provides seamless access to Microsoft Graph APIs from Teams bots and agents built with the Microsoft Teams SDK for Python.
13
+
14
+ <a href="https://microsoft.github.io/teams-sdk" target="_blank">
15
+ <img src="https://img.shields.io/badge/📖 Getting Started-blue?style=for-the-badge" />
16
+ </a>
17
+
18
+ ## Key Features
19
+
20
+ - **Token Integration**: Unified token handling using the Token type from microsoft-teams-common
21
+ - **Flexible Token Sources**: Supports strings, StringLike objects, callables, async callables, or None
22
+ - **Automatic Token Resolution**: Leverages common resolve_token utility for consistent handling
23
+
24
+ ## Requirements
25
+
26
+ - Teams SDK for Python
27
+ - Microsoft Graph SDK for Python (msgraph-sdk)
28
+ - Azure Core library (azure-core)
29
+ - Microsoft Teams Common library (microsoft-teams-common)
30
+
31
+ ## Quick Start
32
+
33
+ ### Basic Usage with Teams Bot
34
+
35
+ ```python
36
+ from microsoft_teams.graph import get_graph_client
37
+ from microsoft_teams.apps import App, ActivityContext
38
+ from microsoft_teams.api import MessageActivity
39
+
40
+ app = App()
41
+
42
+ @app.on_message
43
+ async def handle_message(ctx: ActivityContext[MessageActivity]):
44
+ if not ctx.is_signed_in:
45
+ await ctx.sign_in()
46
+ return
47
+
48
+ # Create Graph client using user's token
49
+ graph = get_graph_client(ctx.user_token)
50
+
51
+ # Get user profile
52
+ me = await graph.me.get()
53
+ await ctx.send(f"Hello {me.display_name}!")
54
+
55
+ # Get user's Teams
56
+ teams = await graph.me.joined_teams.get()
57
+ if teams and teams.value:
58
+ team_names = [team.display_name for team in teams.value]
59
+ await ctx.send(f"You're in {len(team_names)} teams: {', '.join(team_names)}")
60
+ ```
61
+
62
+ ### Token Integration
63
+
64
+ ```python
65
+ from microsoft_teams.common.http.client_token import Token
66
+
67
+ def create_token_callable(ctx: ActivityContext) -> Token:
68
+ """Create a callable token that refreshes automatically."""
69
+ def get_fresh_token():
70
+ # This is called on each Graph API request
71
+ return ctx.user_token # Always returns current valid token
72
+
73
+ return get_fresh_token
74
+
75
+ # Use with Graph client
76
+ graph = get_graph_client(create_token_callable(ctx))
77
+ ```
78
+
79
+ await ctx.sign_in()
80
+ return
81
+
82
+ # Use the user token that's already available in the context
83
+ graph = get_graph_client(ctx.user_token)
84
+
85
+ # Make Graph API calls
86
+ me = await graph.me.get()
87
+ await ctx.send(f"Hello {me.display_name}!")
88
+
89
+ # Make Graph API calls
90
+ me = await graph.me.get()
91
+ await ctx.send(f"Hello {me.display_name}!")
92
+
93
+ ````
94
+
95
+ ## Token Type Usage
96
+
97
+ The package uses the Token type from microsoft-teams-common for flexible token handling. You can provide tokens in several formats:
98
+
99
+ ### String Token (Simplest)
100
+
101
+ ```python
102
+ # Direct string token
103
+ graph = get_graph_client("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...")
104
+ ````
105
+
106
+ ### Callable Token (Dynamic)
107
+
108
+ ```python
109
+ def get_token():
110
+ """Callable that returns a string token."""
111
+ # Get your access token from wherever (Teams API, cache, etc.)
112
+ return get_access_token_from_somewhere()
113
+
114
+ # Use the callable with get_graph_client
115
+ graph = get_graph_client(get_token)
116
+ ```
117
+
118
+ ### Async Callable Token
119
+
120
+ ```python
121
+ async def get_token_async():
122
+ """Async callable that returns a string token."""
123
+ # Fetch token asynchronously
124
+ token_response = await some_api_call()
125
+ return token_response.access_token
126
+
127
+ graph = get_graph_client(get_token_async)
128
+ ```
129
+
130
+ ### Dynamic Token Retrieval
131
+
132
+ ```python
133
+ def get_fresh_token():
134
+ """Callable that fetches a fresh token on each invocation."""
135
+ # This will be called each time the Graph client needs a token
136
+ fresh_token = fetch_latest_token_from_api()
137
+ return fresh_token
138
+
139
+ graph = get_graph_client(get_fresh_token)
140
+ ```
141
+
142
+ ## Authentication
143
+
144
+ The package uses Token-based authentication with automatic resolution through the common library. Teams tokens are pre-authorized through the OAuth connection configured in your Azure Bot registration.
145
+
146
+ ## API Usage Examples
147
+
148
+ ```python
149
+ # Get user profile
150
+ me = await graph.me.get()
151
+
152
+ # Get recent emails with specific fields
153
+ from msgraph.generated.users.item.messages.messages_request_builder import MessagesRequestBuilder
154
+
155
+ query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
156
+ select=["subject", "from", "receivedDateTime"],
157
+ top=5
158
+ )
159
+ request_config = MessagesRequestBuilder.MessagesRequestBuilderGetRequestConfiguration(
160
+ query_parameters=query_params
161
+ )
162
+ messages = await graph.me.messages.get(request_configuration=request_config)
163
+ ```
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "microsoft-teams-graph"
3
+ version = "2.0.0a9"
4
+ description = "The Graph package for a Microsoft Teams agent"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [{ name = "Microsoft", email = "TeamsAISDKFeedback@microsoft.com" }]
8
+ keywords = ["microsoft", "teams", "ai", "bot", "agents", "graph"]
9
+ requires-python = ">=3.12,<3.15"
10
+ dependencies = [
11
+ "azure-core>=1.31.0",
12
+ "msgraph-sdk>=1.30.0,<2.0.0",
13
+ "microsoft-teams-common",
14
+ "pyjwt[crypto]>=2.10.0",
15
+ ]
16
+
17
+ [dependency-groups]
18
+ dev = [
19
+ "pytest>=8.4.0",
20
+ "pytest-asyncio>=1.0.0",
21
+ ]
22
+
23
+ [tool.pytest.ini_options]
24
+ asyncio_mode = "auto"
25
+ testpaths = ["tests"]
26
+ python_paths = ["src"]
27
+
28
+ [build-system]
29
+ requires = ["hatchling"]
30
+ build-backend = "hatchling.build"
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["src/microsoft_teams", "src/microsoft"]
34
+
35
+ [tool.hatch.build.targets.sdist]
36
+ include = ["src"]
37
+
38
+ [tool.uv.sources]
39
+ microsoft-teams-common = { workspace = true }
@@ -0,0 +1,26 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+
5
+ Backward compatibility shim for microsoft.teams.graph.
6
+
7
+
8
+ DEPRECATED: This import path is deprecated and will be removed in version 2.0.0 GA.
9
+ Please update your imports to use 'microsoft_teams.graph' instead.
10
+ """
11
+
12
+ import sys
13
+ import warnings
14
+
15
+ warnings.warn(
16
+ "The 'microsoft.teams.graph' namespace is deprecated and will be removed in "
17
+ "version 2.0.0 GA. Please update your imports to 'microsoft_teams.graph'.",
18
+ DeprecationWarning,
19
+ stacklevel=2,
20
+ )
21
+
22
+ from microsoft_teams.graph import * # noqa: E402, F401, F403
23
+ from microsoft_teams.graph import __all__ # noqa: E402, F401
24
+
25
+ _new_module = sys.modules["microsoft_teams.graph"]
26
+ sys.modules[__name__] = _new_module
@@ -0,0 +1,12 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+ """
5
+
6
+ from .auth_provider import AuthProvider
7
+ from .graph import get_graph_client
8
+
9
+ __all__ = [
10
+ "AuthProvider",
11
+ "get_graph_client",
12
+ ]
@@ -0,0 +1,87 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+ """
5
+
6
+ import asyncio
7
+ import concurrent.futures
8
+ import datetime
9
+ import logging
10
+ from typing import Any
11
+
12
+ import jwt
13
+ from azure.core.credentials import AccessToken, TokenCredential
14
+ from azure.core.exceptions import ClientAuthenticationError
15
+ from microsoft_teams.common.http.client_token import Token, resolve_token
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class AuthProvider(TokenCredential):
21
+ """
22
+ Provides authentication for Microsoft Graph using Teams tokens.
23
+
24
+ """
25
+
26
+ def __init__(self, token: Token) -> None:
27
+ """
28
+ Initialize the AuthProvider.
29
+
30
+ Args:
31
+ token: Token data (string, StringLike, callable, or None)
32
+ """
33
+ self._token = token
34
+
35
+ def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken:
36
+ """
37
+ Retrieve an access token for Microsoft Graph.
38
+
39
+ Args:
40
+ *scopes: Token scopes (required for interface compatibility)
41
+ **kwargs: Additional keyword arguments
42
+
43
+ Returns:
44
+ AccessToken: The access token for Microsoft Graph
45
+
46
+ Raises:
47
+ ClientAuthenticationError: If the token is invalid or authentication fails
48
+ """
49
+ try:
50
+ # Resolve the token using the common utility
51
+ try:
52
+ asyncio.get_running_loop()
53
+
54
+ with concurrent.futures.ThreadPoolExecutor() as executor:
55
+ future = executor.submit(asyncio.run, resolve_token(self._token))
56
+ token_str = future.result()
57
+ except RuntimeError:
58
+ token_str = asyncio.run(resolve_token(self._token))
59
+
60
+ if not token_str:
61
+ raise ClientAuthenticationError("Token resolved to None or empty string")
62
+
63
+ if not token_str.strip():
64
+ raise ClientAuthenticationError("Token contains only whitespace")
65
+
66
+ # Try to extract expiration from JWT, fallback to 1 hour default
67
+ try:
68
+ # Decode JWT without verification to extract expiration
69
+ payload = jwt.decode(token_str, algorithms=["RS256"], options={"verify_signature": False})
70
+ expires_on = payload.get("exp")
71
+ if not expires_on:
72
+ # Fallback to 1 hour from now if no exp claim
73
+ logger.debug("JWT token missing 'exp' claim, using 1-hour default expiration")
74
+ now = datetime.datetime.now(datetime.timezone.utc)
75
+ expires_on = int((now + datetime.timedelta(hours=1)).timestamp())
76
+ except Exception:
77
+ # Fallback to 1 hour from now if JWT decoding fails (e.g., not a JWT)
78
+ logger.debug("Token is not a valid JWT, using 1-hour default expiration")
79
+ now = datetime.datetime.now(datetime.timezone.utc)
80
+ expires_on = int((now + datetime.timedelta(hours=1)).timestamp())
81
+
82
+ return AccessToken(token=token_str, expires_on=expires_on)
83
+
84
+ except Exception as e:
85
+ if isinstance(e, ClientAuthenticationError):
86
+ raise
87
+ raise ClientAuthenticationError(f"Failed to resolve token: {str(e)}") from e
@@ -0,0 +1,62 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+ """
5
+
6
+ from typing import Optional
7
+
8
+ from azure.core.exceptions import ClientAuthenticationError
9
+ from microsoft_teams.common.http.client_token import Token
10
+ from msgraph.graph_service_client import GraphServiceClient
11
+
12
+ from .auth_provider import AuthProvider
13
+
14
+
15
+ def get_graph_client(token: Optional[Token] = None) -> GraphServiceClient:
16
+ """
17
+ Get a configured Microsoft Graph client using a Token.
18
+
19
+ Args:
20
+ token: Token data (string, StringLike, callable, or None). If None,
21
+ will raise ClientAuthenticationError with a clear message.
22
+
23
+ Returns:
24
+ GraphServiceClient: A configured client ready for Microsoft Graph API calls
25
+
26
+ Raises:
27
+ ClientAuthenticationError: If the token is None, invalid, or authentication fails
28
+
29
+ Example:
30
+ ```python
31
+ # Using a string token
32
+ graph = get_graph_client("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...")
33
+
34
+
35
+ # Using a callable that returns a string
36
+ def get_token():
37
+ return "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
38
+
39
+
40
+ graph = get_graph_client(get_token)
41
+
42
+ # Make Graph API calls
43
+ me = await graph.me.get()
44
+ messages = await graph.me.messages.get()
45
+ ```
46
+ """
47
+ try:
48
+ # Provide a clear error message for None tokens
49
+ if token is None:
50
+ raise ClientAuthenticationError(
51
+ "Token cannot be None. Please provide a valid token (string, callable, or StringLike object) "
52
+ "to authenticate with Microsoft Graph."
53
+ )
54
+
55
+ credential = AuthProvider(token)
56
+ client = GraphServiceClient(credentials=credential)
57
+ return client
58
+
59
+ except Exception as e:
60
+ if isinstance(e, ClientAuthenticationError):
61
+ raise # Re-raise authentication errors as-is
62
+ raise ClientAuthenticationError(f"Failed to create Microsoft Graph client: {str(e)}") from e