PagerDuty typed OAuth object (#718)
<!-- CURSOR_SUMMARY --> > [!NOTE] > Adds a typed `PagerDuty` OAuth2 provider and wires it through TDK/MCP exports, with tests and coordinated version/dependency bumps. > > - **Auth (core)**: > - Add typed OAuth2 provider `PagerDuty` (`provider_id="pagerduty"`) in `arcade_core/auth.py`. > - **TDK & MCP Server**: > - Re-export `PagerDuty` in `arcade_tdk/auth/__init__.py` and `arcade_mcp_server/auth/__init__.py`. > - **Tests**: > - Extend `test_tool_decorator.py` and `test_create_tool_definition.py` to cover `PagerDuty` success/failure and tool requirement generation. > - **Versioning/Deps**: > - Bump versions: `arcade-core`→`4.1.0`, `arcade-tdk`→`3.4.0`, `arcade-mcp-server`→`1.14.0`, root `arcade-mcp`→`1.7.1`. > - Update dependency ranges to require the bumped versions. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2b60261b1962586ea58831ccb6ea66e57053ac86. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Francisco Liberal <francisco@arcade.dev>
This commit is contained in:
parent
0fb3b75b8b
commit
7a06bdfa7e
9 changed files with 86 additions and 45 deletions
|
|
@ -150,6 +150,15 @@ class Notion(OAuth2):
|
||||||
super().__init__(id=id, scopes=scopes)
|
super().__init__(id=id, scopes=scopes)
|
||||||
|
|
||||||
|
|
||||||
|
class PagerDuty(OAuth2):
|
||||||
|
"""Marks a tool as requiring PagerDuty authorization."""
|
||||||
|
|
||||||
|
provider_id: str = "pagerduty"
|
||||||
|
|
||||||
|
def __init__(self, *, id: Optional[str] = None, scopes: Optional[list[str]] = None): # noqa: A002
|
||||||
|
super().__init__(id=id, scopes=scopes)
|
||||||
|
|
||||||
|
|
||||||
class Reddit(OAuth2):
|
class Reddit(OAuth2):
|
||||||
"""Marks a tool as requiring Reddit authorization."""
|
"""Marks a tool as requiring Reddit authorization."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
[project]
|
[project]
|
||||||
name = "arcade-core"
|
name = "arcade-core"
|
||||||
version = "4.0.0"
|
version = "4.1.0"
|
||||||
description = "Arcade Core - Core library for Arcade platform"
|
description = "Arcade Core - Core library for Arcade platform"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {text = "MIT"}
|
license = { text = "MIT" }
|
||||||
authors = [
|
authors = [{ name = "Arcade", email = "dev@arcade.dev" }]
|
||||||
{name = "Arcade", email = "dev@arcade.dev"},
|
|
||||||
]
|
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from arcade_core.auth import (
|
||||||
Microsoft,
|
Microsoft,
|
||||||
Notion,
|
Notion,
|
||||||
OAuth2,
|
OAuth2,
|
||||||
|
PagerDuty,
|
||||||
Reddit,
|
Reddit,
|
||||||
Slack,
|
Slack,
|
||||||
Spotify,
|
Spotify,
|
||||||
|
|
@ -36,6 +37,7 @@ __all__ = [
|
||||||
"LinkedIn",
|
"LinkedIn",
|
||||||
"Microsoft",
|
"Microsoft",
|
||||||
"Notion",
|
"Notion",
|
||||||
|
"PagerDuty",
|
||||||
"OAuth2",
|
"OAuth2",
|
||||||
"Reddit",
|
"Reddit",
|
||||||
"Slack",
|
"Slack",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade-mcp-server"
|
name = "arcade-mcp-server"
|
||||||
version = "1.13.0"
|
version = "1.14.0"
|
||||||
description = "Model Context Protocol (MCP) server framework for Arcade.dev"
|
description = "Model Context Protocol (MCP) server framework for Arcade.dev"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{ name = "Arcade.dev" }]
|
authors = [{ name = "Arcade.dev" }]
|
||||||
|
|
@ -21,14 +21,14 @@ classifiers = [
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-core>=4.0.0,<5.0.0",
|
"arcade-core>=4.1.0,<5.0.0",
|
||||||
"arcade-serve>=3.2.0,<4.0.0",
|
"arcade-serve>=3.2.0,<4.0.0",
|
||||||
"arcade-tdk>=3.3.0,<4.0.0",
|
"arcade-tdk>=3.4.0,<4.0.0",
|
||||||
"arcadepy>=1.5.0",
|
"arcadepy>=1.5.0",
|
||||||
"pydantic>=2.0.0",
|
"pydantic>=2.0.0",
|
||||||
"fastapi>=0.100.0",
|
"fastapi>=0.100.0",
|
||||||
"uvicorn>=0.30.0",
|
"uvicorn>=0.30.0",
|
||||||
"watchfiles>=0.18.0", # included with uvicorn, but listed to be explicit
|
"watchfiles>=0.18.0", # included with uvicorn, but listed to be explicit
|
||||||
"sse-starlette>=2.0.0",
|
"sse-starlette>=2.0.0",
|
||||||
"starlette>=0.37.0",
|
"starlette>=0.37.0",
|
||||||
"anyio>=4.0.0",
|
"anyio>=4.0.0",
|
||||||
|
|
@ -39,12 +39,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = ["pytest>=8.0.0", "pytest-asyncio>=0.23.0", "mypy>=1.0.0", "ruff>=0.1.0"]
|
||||||
"pytest>=8.0.0",
|
|
||||||
"pytest-asyncio>=0.23.0",
|
|
||||||
"mypy>=1.0.0",
|
|
||||||
"ruff>=0.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["arcade_mcp_server"]
|
packages = ["arcade_mcp_server"]
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from arcade_core.auth import (
|
||||||
Microsoft,
|
Microsoft,
|
||||||
Notion,
|
Notion,
|
||||||
OAuth2,
|
OAuth2,
|
||||||
|
PagerDuty,
|
||||||
Reddit,
|
Reddit,
|
||||||
Slack,
|
Slack,
|
||||||
Spotify,
|
Spotify,
|
||||||
|
|
@ -36,6 +37,7 @@ __all__ = [
|
||||||
"LinkedIn",
|
"LinkedIn",
|
||||||
"Microsoft",
|
"Microsoft",
|
||||||
"Notion",
|
"Notion",
|
||||||
|
"PagerDuty",
|
||||||
"OAuth2",
|
"OAuth2",
|
||||||
"Reddit",
|
"Reddit",
|
||||||
"Slack",
|
"Slack",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
[project]
|
[project]
|
||||||
name = "arcade-tdk"
|
name = "arcade-tdk"
|
||||||
version = "3.3.0"
|
version = "3.4.0"
|
||||||
description = "Arcade TDK - Toolkit Development Kit for building Arcade tools"
|
description = "Arcade TDK - Toolkit Development Kit for building Arcade tools"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {text = "MIT"}
|
license = { text = "MIT" }
|
||||||
authors = [
|
authors = [{ name = "Arcade", email = "dev@arcade.dev" }]
|
||||||
{name = "Arcade", email = "dev@arcade.dev"},
|
|
||||||
]
|
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
|
|
@ -18,10 +16,7 @@ classifiers = [
|
||||||
"Programming Language :: Python :: 3.13",
|
"Programming Language :: Python :: 3.13",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = ["arcade-core>=4.1.0,<5.0.0", "pydantic>=2.7.0"]
|
||||||
"arcade-core>=4.0.0,<5.0.0",
|
|
||||||
"pydantic>=2.7.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import asyncio
|
||||||
import pytest
|
import pytest
|
||||||
from arcade_core.auth import AuthProviderType, Google
|
from arcade_core.auth import AuthProviderType, Google
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
from arcade_tdk.auth import OAuth2
|
from arcade_tdk.auth import OAuth2, PagerDuty
|
||||||
|
|
||||||
|
|
||||||
def test_sync_function():
|
def test_sync_function():
|
||||||
|
|
@ -50,6 +50,13 @@ async def test_async_function():
|
||||||
"google",
|
"google",
|
||||||
"my_google_provider123",
|
"my_google_provider123",
|
||||||
),
|
),
|
||||||
|
(PagerDuty, {"scopes": ["test_scope", "another.scope"]}, "pagerduty", None),
|
||||||
|
(
|
||||||
|
PagerDuty,
|
||||||
|
{"id": "my_pagerduty_provider123", "scopes": ["test_scope", "another.scope"]},
|
||||||
|
"pagerduty",
|
||||||
|
"my_pagerduty_provider123",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_tool_decorator_with_auth_success(
|
def test_tool_decorator_with_auth_success(
|
||||||
|
|
@ -102,6 +109,21 @@ def test_tool_decorator_with_auth_success(
|
||||||
"scopes": ["test_scope", "another.scope"],
|
"scopes": ["test_scope", "another.scope"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
PagerDuty,
|
||||||
|
{
|
||||||
|
"provider_id": "my_example_provider_id_123",
|
||||||
|
"scopes": ["test_scope", "another.scope"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PagerDuty,
|
||||||
|
{
|
||||||
|
"provider_id": "my_example_provider_id_123",
|
||||||
|
"id": "my_example_id_123",
|
||||||
|
"scopes": ["test_scope", "another.scope"],
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_tool_decorator_with_auth_failure(auth_class, auth_kwargs):
|
def test_tool_decorator_with_auth_failure(auth_class, auth_kwargs):
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ from arcade_core.schema import (
|
||||||
from arcade_core.utils import snake_to_pascal_case
|
from arcade_core.utils import snake_to_pascal_case
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
from arcade_tdk.annotations import Inferrable
|
from arcade_tdk.annotations import Inferrable
|
||||||
from arcade_tdk.auth import Figma, GitHub, Google, OAuth2, Slack, X
|
from arcade_tdk.auth import Figma, GitHub, Google, OAuth2, PagerDuty, Slack, X
|
||||||
|
|
||||||
|
|
||||||
### Tests on @tool decorator
|
### Tests on @tool decorator
|
||||||
|
|
@ -159,6 +159,17 @@ def func_with_x_requirement():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@tool(
|
||||||
|
desc="A function that requires PagerDuty authorization",
|
||||||
|
requires_auth=PagerDuty(
|
||||||
|
id="my_pagerduty_provider123",
|
||||||
|
scopes=["read", "write"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def func_with_pagerduty_auth_requirement():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
### Tests on input params
|
### Tests on input params
|
||||||
@tool(desc="A function with a non-inferrable input parameter")
|
@tool(desc="A function with a non-inferrable input parameter")
|
||||||
def func_with_non_inferrable_param(param1: Annotated[str, "First param", Inferrable(False)]):
|
def func_with_non_inferrable_param(param1: Annotated[str, "First param", Inferrable(False)]):
|
||||||
|
|
@ -507,6 +518,22 @@ def func_with_complex_return() -> dict[str, str]:
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
pytest.param(
|
||||||
|
func_with_pagerduty_auth_requirement,
|
||||||
|
{
|
||||||
|
"requirements": ToolRequirements(
|
||||||
|
authorization=ToolAuthRequirement(
|
||||||
|
provider_id="pagerduty",
|
||||||
|
provider_type="oauth2",
|
||||||
|
id="my_pagerduty_provider123",
|
||||||
|
oauth2=OAuth2Requirement(
|
||||||
|
scopes=["read", "write"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
id="func_with_pagerduty_auth_requirement",
|
||||||
|
),
|
||||||
# Tests on input params
|
# Tests on input params
|
||||||
pytest.param(
|
pytest.param(
|
||||||
func_with_non_inferrable_param,
|
func_with_non_inferrable_param,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
[project]
|
[project]
|
||||||
name = "arcade-mcp"
|
name = "arcade-mcp"
|
||||||
version = "1.7.0"
|
version = "1.7.1"
|
||||||
description = "Arcade.dev - Tool Calling platform for Agents"
|
description = "Arcade.dev - Tool Calling platform for Agents"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {file = "LICENSE"}
|
license = { file = "LICENSE" }
|
||||||
authors = [
|
authors = [{ name = "Arcade", email = "dev@arcade.dev" }]
|
||||||
{name = "Arcade", email = "dev@arcade.dev"},
|
|
||||||
]
|
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
|
|
@ -21,8 +19,8 @@ requires-python = ">=3.10"
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
# CLI dependencies
|
# CLI dependencies
|
||||||
"arcade-mcp-server>=1.13.0,<2.0.0",
|
"arcade-mcp-server>=1.14.0,<2.0.0",
|
||||||
"arcade-core>=4.0.0,<5.0.0",
|
"arcade-core>=4.1.0,<5.0.0",
|
||||||
"typer==0.10.0",
|
"typer==0.10.0",
|
||||||
"rich>=14.0.0,<15.0.0",
|
"rich>=14.0.0,<15.0.0",
|
||||||
"Jinja2==3.1.6",
|
"Jinja2==3.1.6",
|
||||||
|
|
@ -43,11 +41,11 @@ all = [
|
||||||
"pytz>=2024.1",
|
"pytz>=2024.1",
|
||||||
"python-dateutil>=2.8.2",
|
"python-dateutil>=2.8.2",
|
||||||
# mcp
|
# mcp
|
||||||
"arcade-mcp-server>=1.13.0,<2.0.0",
|
"arcade-mcp-server>=1.14.0,<2.0.0",
|
||||||
# serve
|
# serve
|
||||||
"arcade-serve>=3.2.0,<4.0.0",
|
"arcade-serve>=3.2.0,<4.0.0",
|
||||||
# tdk
|
# tdk
|
||||||
"arcade-tdk>=3.3.0,<4.0.0",
|
"arcade-tdk>=3.4.0,<4.0.0",
|
||||||
]
|
]
|
||||||
# Evals also depends on arcade-core and openai, but they are already required deps
|
# Evals also depends on arcade-core and openai, but they are already required deps
|
||||||
evals = [
|
evals = [
|
||||||
|
|
@ -89,10 +87,7 @@ requires = ["hatchling"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = [
|
packages = ["libs/arcade-cli/arcade_cli", "libs/arcade-evals/arcade_evals"]
|
||||||
"libs/arcade-cli/arcade_cli",
|
|
||||||
"libs/arcade-evals/arcade_evals",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.uv.workspace]
|
[tool.uv.workspace]
|
||||||
members = [
|
members = [
|
||||||
|
|
@ -113,7 +108,7 @@ warn_unused_ignores = true
|
||||||
show_error_codes = true
|
show_error_codes = true
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
exclude = [
|
exclude = [
|
||||||
'.*{{.*}}.*' # Ignore files that have names that use Jinja template syntax
|
'.*{{.*}}.*', # Ignore files that have names that use Jinja template syntax
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
|
|
@ -133,11 +128,7 @@ addopts = [
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
source = ["libs"]
|
source = ["libs"]
|
||||||
omit = [
|
omit = ["*/tests/*", "*/test_*", "*/__pycache__/*"]
|
||||||
"*/tests/*",
|
|
||||||
"*/test_*",
|
|
||||||
"*/__pycache__/*",
|
|
||||||
]
|
|
||||||
parallel = true
|
parallel = true
|
||||||
patch = ["subprocess"]
|
patch = ["subprocess"]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue