Add ToolMetadata to OSS toolkits (#776)
Resolves TOO-388 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Primarily metadata/dependency additions with no changes to core tool execution paths; risk is limited to potential packaging/import issues from the new `arcade-mcp-server` dependency. > > **Overview** > Adds `ToolMetadata` to tool decorators across the Bright Data, ClickHouse, MongoDB, Postgres, LinkedIn, Zendesk, and Math toolkits, specifying *behavior* (read-only/idempotency/destructive/open-world) and, where applicable, *service domain* classification. > > Updates each toolkit package to depend on `arcade-mcp-server` (plus local `uv` source wiring) and bumps toolkit versions accordingly; minor `__all__` ordering tweaks in Math/Zendesk are included. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3bde3a061194e1d1b6a4e8a2ebd608b17984db4f. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This commit is contained in:
parent
fe8ddfd500
commit
5228c75dc9
26 changed files with 625 additions and 67 deletions
|
|
@ -5,6 +5,13 @@ from typing import Annotated, Any, cast
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from arcade_core.errors import RetryableToolError
|
from arcade_core.errors import RetryableToolError
|
||||||
|
from arcade_mcp_server.metadata import (
|
||||||
|
Behavior,
|
||||||
|
Classification,
|
||||||
|
Operation,
|
||||||
|
ServiceDomain,
|
||||||
|
ToolMetadata,
|
||||||
|
)
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
|
|
||||||
from arcade_brightdata.bright_data_client import BrightDataClient
|
from arcade_brightdata.bright_data_client import BrightDataClient
|
||||||
|
|
@ -51,7 +58,21 @@ class SourceType(str, Enum):
|
||||||
YOUTUBE_VIDEOS = "youtube_videos"
|
YOUTUBE_VIDEOS = "youtube_videos"
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["BRIGHTDATA_API_KEY", "BRIGHTDATA_ZONE"])
|
@tool(
|
||||||
|
requires_secrets=["BRIGHTDATA_API_KEY", "BRIGHTDATA_ZONE"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.WEB_SCRAPING],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def scrape_as_markdown(
|
def scrape_as_markdown(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
url: Annotated[str, "URL to scrape"],
|
url: Annotated[str, "URL to scrape"],
|
||||||
|
|
@ -71,7 +92,21 @@ def scrape_as_markdown(
|
||||||
return client.make_request(payload)
|
return client.make_request(payload)
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["BRIGHTDATA_API_KEY", "BRIGHTDATA_ZONE"])
|
@tool(
|
||||||
|
requires_secrets=["BRIGHTDATA_API_KEY", "BRIGHTDATA_ZONE"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.WEB_SCRAPING],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def search_engine( # noqa: C901
|
def search_engine( # noqa: C901
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
query: Annotated[str, "Search query"],
|
query: Annotated[str, "Search query"],
|
||||||
|
|
@ -167,7 +202,21 @@ def search_engine( # noqa: C901
|
||||||
return client.make_request(payload)
|
return client.make_request(payload)
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["BRIGHTDATA_API_KEY"])
|
@tool(
|
||||||
|
requires_secrets=["BRIGHTDATA_API_KEY"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.WEB_SCRAPING],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def web_data_feed(
|
def web_data_feed(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
source_type: Annotated[SourceType, "Type of data source"],
|
source_type: Annotated[SourceType, "Type of data source"],
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_brightdata"
|
name = "arcade_brightdata"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
description = "Search, Crawl and Scrape any site, at scale, without getting blocked"
|
description = "Search, Crawl and Scrape any site, at scale, without getting blocked"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"requests>=2.32.5",
|
"requests>=2.32.5",
|
||||||
]
|
]
|
||||||
[[project.authors]]
|
[[project.authors]]
|
||||||
|
|
@ -49,6 +50,7 @@ ignore_missing_imports = "True"
|
||||||
arcade-mcp = { path = "../../", editable = true }
|
arcade-mcp = { path = "../../", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = [ "tests",]
|
testpaths = [ "tests",]
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,24 @@
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, Operation, ToolMetadata
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.errors import RetryableToolError
|
from arcade_tdk.errors import RetryableToolError
|
||||||
|
|
||||||
from ..database_engine import MAX_ROWS_RETURNED, DatabaseEngine
|
from ..database_engine import MAX_ROWS_RETURNED, DatabaseEngine
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_schemas(
|
async def discover_schemas(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
|
|
@ -18,7 +30,18 @@ async def discover_schemas(
|
||||||
return ["default"]
|
return ["default"]
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_databases(
|
async def discover_databases(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
|
|
@ -30,7 +53,18 @@ async def discover_databases(
|
||||||
return databases
|
return databases
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_tables(
|
async def discover_tables(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
|
|
@ -45,7 +79,18 @@ async def discover_tables(
|
||||||
return tables
|
return tables
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def get_table_schema(
|
async def get_table_schema(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
schema_name: Annotated[str, "The schema to get the table schema of"],
|
schema_name: Annotated[str, "The schema to get the table schema of"],
|
||||||
|
|
@ -62,7 +107,18 @@ async def get_table_schema(
|
||||||
return await _get_table_schema(client, "default", table_name)
|
return await _get_table_schema(client, "default", table_name)
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["CLICKHOUSE_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def execute_select_query(
|
async def execute_select_query(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
select_clause: Annotated[
|
select_clause: Annotated[
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_clickhouse"
|
name = "arcade_clickhouse"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "Tools to query and explore a ClickHouse database"
|
description = "Tools to query and explore a ClickHouse database"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"clickhouse-connect>=0.7.0",
|
"clickhouse-connect>=0.7.0",
|
||||||
"pydantic>=2.11.7",
|
"pydantic>=2.11.7",
|
||||||
"sqlalchemy>=2.0.41",
|
"sqlalchemy>=2.0.41",
|
||||||
|
|
@ -41,6 +42,7 @@ dev = [
|
||||||
arcade-mcp = { path = "../../", editable = true }
|
arcade-mcp = { path = "../../", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import (
|
||||||
|
Behavior,
|
||||||
|
Classification,
|
||||||
|
Operation,
|
||||||
|
ServiceDomain,
|
||||||
|
ToolMetadata,
|
||||||
|
)
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.auth import LinkedIn
|
from arcade_tdk.auth import LinkedIn
|
||||||
from arcade_tdk.errors import ToolExecutionError
|
from arcade_tdk.errors import ToolExecutionError
|
||||||
|
|
@ -10,7 +17,19 @@ from arcade_linkedin.tools.utils import _handle_linkedin_api_error, _send_linked
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=LinkedIn(
|
requires_auth=LinkedIn(
|
||||||
scopes=["w_member_social"],
|
scopes=["w_member_social"],
|
||||||
)
|
),
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.SOCIAL_MEDIA],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.CREATE],
|
||||||
|
read_only=False,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def create_text_post(
|
async def create_text_post(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_linkedin"
|
name = "arcade_linkedin"
|
||||||
version = "0.1.14"
|
version = "0.2.0"
|
||||||
description = "Arcade.dev LLM tools for LinkedIn"
|
description = "Arcade.dev LLM tools for LinkedIn"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"httpx>=0.27.2,<1.0.0",
|
"httpx>=0.27.2,<1.0.0",
|
||||||
]
|
]
|
||||||
[[project.authors]]
|
[[project.authors]]
|
||||||
|
|
@ -34,6 +35,7 @@ dev = [
|
||||||
arcade-mcp = {path = "../../", editable = true}
|
arcade-mcp = {path = "../../", editable = true}
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
files = [ "arcade_linkedin/**/*.py",]
|
files = [ "arcade_linkedin/**/*.py",]
|
||||||
|
|
|
||||||
|
|
@ -39,27 +39,27 @@ from arcade_math.tools.trigonometry import (
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"add",
|
|
||||||
"subtract",
|
|
||||||
"multiply",
|
|
||||||
"divide",
|
|
||||||
"sum_list",
|
|
||||||
"sum_range",
|
|
||||||
"mod",
|
|
||||||
"log",
|
|
||||||
"power",
|
|
||||||
"abs_val",
|
"abs_val",
|
||||||
|
"add",
|
||||||
|
"avg",
|
||||||
|
"ceil",
|
||||||
|
"deg_to_rad",
|
||||||
|
"divide",
|
||||||
"factorial",
|
"factorial",
|
||||||
"sqrt",
|
"floor",
|
||||||
|
"gcd",
|
||||||
"generate_random_float",
|
"generate_random_float",
|
||||||
"generate_random_int",
|
"generate_random_int",
|
||||||
"gcd",
|
|
||||||
"lcm",
|
"lcm",
|
||||||
"ceil",
|
"log",
|
||||||
"floor",
|
|
||||||
"round_num",
|
|
||||||
"avg",
|
|
||||||
"median",
|
"median",
|
||||||
"deg_to_rad",
|
"mod",
|
||||||
|
"multiply",
|
||||||
|
"power",
|
||||||
"rad_to_deg",
|
"rad_to_deg",
|
||||||
|
"round_num",
|
||||||
|
"sqrt",
|
||||||
|
"subtract",
|
||||||
|
"sum_list",
|
||||||
|
"sum_range",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,22 @@ import decimal
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def add(
|
def add(
|
||||||
a: Annotated[str, "The first number as a string"],
|
a: Annotated[str, "The first number as a string"],
|
||||||
b: Annotated[str, "The second number as a string"],
|
b: Annotated[str, "The second number as a string"],
|
||||||
|
|
@ -21,7 +31,16 @@ def add(
|
||||||
return str(a_decimal + b_decimal)
|
return str(a_decimal + b_decimal)
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def subtract(
|
def subtract(
|
||||||
a: Annotated[str, "The first number as a string"],
|
a: Annotated[str, "The first number as a string"],
|
||||||
b: Annotated[str, "The second number as a string"],
|
b: Annotated[str, "The second number as a string"],
|
||||||
|
|
@ -35,7 +54,16 @@ def subtract(
|
||||||
return str(a_decimal - b_decimal)
|
return str(a_decimal - b_decimal)
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def multiply(
|
def multiply(
|
||||||
a: Annotated[str, "The first number as a string"],
|
a: Annotated[str, "The first number as a string"],
|
||||||
b: Annotated[str, "The second number as a string"],
|
b: Annotated[str, "The second number as a string"],
|
||||||
|
|
@ -49,7 +77,16 @@ def multiply(
|
||||||
return str(a_decimal * b_decimal)
|
return str(a_decimal * b_decimal)
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def divide(
|
def divide(
|
||||||
a: Annotated[str, "The first number as a string"],
|
a: Annotated[str, "The first number as a string"],
|
||||||
b: Annotated[str, "The second number as a string"],
|
b: Annotated[str, "The second number as a string"],
|
||||||
|
|
@ -63,7 +100,16 @@ def divide(
|
||||||
return str(a_decimal / b_decimal)
|
return str(a_decimal / b_decimal)
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def sum_list(
|
def sum_list(
|
||||||
numbers: Annotated[list[str], "The list of numbers as strings"],
|
numbers: Annotated[list[str], "The list of numbers as strings"],
|
||||||
) -> Annotated[str, "The sum of the numbers in the list as a string"]:
|
) -> Annotated[str, "The sum of the numbers in the list as a string"]:
|
||||||
|
|
@ -74,7 +120,16 @@ def sum_list(
|
||||||
return str(sum([Decimal(n) for n in numbers]))
|
return str(sum([Decimal(n) for n in numbers]))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def sum_range(
|
def sum_range(
|
||||||
start: Annotated[str, "The start of the range to sum as a string"],
|
start: Annotated[str, "The start of the range to sum as a string"],
|
||||||
end: Annotated[str, "The end of the range to sum as a string"],
|
end: Annotated[str, "The end of the range to sum as a string"],
|
||||||
|
|
@ -85,7 +140,16 @@ def sum_range(
|
||||||
return str(sum(list(range(int(start), int(end) + 1))))
|
return str(sum(list(range(int(start), int(end) + 1))))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def mod(
|
def mod(
|
||||||
a: Annotated[str, "The dividend as a string"],
|
a: Annotated[str, "The dividend as a string"],
|
||||||
b: Annotated[str, "The divisor as a string"],
|
b: Annotated[str, "The divisor as a string"],
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,22 @@ import math
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def log(
|
def log(
|
||||||
a: Annotated[str, "The number to take the logarithm of as a string"],
|
a: Annotated[str, "The number to take the logarithm of as a string"],
|
||||||
base: Annotated[str, "The logarithmic base as a string"],
|
base: Annotated[str, "The logarithmic base as a string"],
|
||||||
|
|
@ -20,7 +30,16 @@ def log(
|
||||||
return str(math.log(Decimal(a), Decimal(base)))
|
return str(math.log(Decimal(a), Decimal(base)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def power(
|
def power(
|
||||||
a: Annotated[str, "The base number as a string"],
|
a: Annotated[str, "The base number as a string"],
|
||||||
b: Annotated[str, "The exponent as a string"],
|
b: Annotated[str, "The exponent as a string"],
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,22 @@ import math
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def abs_val(
|
def abs_val(
|
||||||
a: Annotated[str, "The number as a string"],
|
a: Annotated[str, "The number as a string"],
|
||||||
) -> Annotated[str, "The absolute value of the number as a string"]:
|
) -> Annotated[str, "The absolute value of the number as a string"]:
|
||||||
|
|
@ -19,7 +29,16 @@ def abs_val(
|
||||||
return str(abs(Decimal(a)))
|
return str(abs(Decimal(a)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def factorial(
|
def factorial(
|
||||||
a: Annotated[str, "The non-negative integer to compute the factorial for as a string"],
|
a: Annotated[str, "The non-negative integer to compute the factorial for as a string"],
|
||||||
) -> Annotated[str, "The factorial of the number as a string"]:
|
) -> Annotated[str, "The factorial of the number as a string"]:
|
||||||
|
|
@ -30,7 +49,16 @@ def factorial(
|
||||||
return str(math.factorial(int(a)))
|
return str(math.factorial(int(a)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def sqrt(
|
def sqrt(
|
||||||
a: Annotated[str, "The number to square root as a string"],
|
a: Annotated[str, "The number to square root as a string"],
|
||||||
) -> Annotated[str, "The square root of the number as a string"]:
|
) -> Annotated[str, "The square root of the number as a string"]:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
import random
|
import random
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def generate_random_int(
|
def generate_random_int(
|
||||||
min_value: Annotated[str, "The minimum value of the random integer as a string"],
|
min_value: Annotated[str, "The minimum value of the random integer as a string"],
|
||||||
max_value: Annotated[str, "The maximum value of the random integer as a string"],
|
max_value: Annotated[str, "The maximum value of the random integer as a string"],
|
||||||
|
|
@ -21,7 +31,16 @@ def generate_random_int(
|
||||||
return str(random.randint(int(min_value), int(max_value))) # noqa: S311
|
return str(random.randint(int(min_value), int(max_value))) # noqa: S311
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def generate_random_float(
|
def generate_random_float(
|
||||||
min_value: Annotated[str, "The minimum value of the random float as a string"],
|
min_value: Annotated[str, "The minimum value of the random float as a string"],
|
||||||
max_value: Annotated[str, "The maximum value of the random float as a string"],
|
max_value: Annotated[str, "The maximum value of the random float as a string"],
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
import math
|
import math
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def gcd(
|
def gcd(
|
||||||
a: Annotated[str, "First integer as a string"],
|
a: Annotated[str, "First integer as a string"],
|
||||||
b: Annotated[str, "Second integer as a string"],
|
b: Annotated[str, "Second integer as a string"],
|
||||||
|
|
@ -15,7 +25,16 @@ def gcd(
|
||||||
return str(math.gcd(int(a), int(b)))
|
return str(math.gcd(int(a), int(b)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def lcm(
|
def lcm(
|
||||||
a: Annotated[str, "First integer as a string"],
|
a: Annotated[str, "First integer as a string"],
|
||||||
b: Annotated[str, "Second integer as a string"],
|
b: Annotated[str, "Second integer as a string"],
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,22 @@ import math
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def ceil(
|
def ceil(
|
||||||
a: Annotated[str, "The number to round up as a string"],
|
a: Annotated[str, "The number to round up as a string"],
|
||||||
) -> Annotated[str, "The smallest integer greater than or equal to the number as a string"]:
|
) -> Annotated[str, "The smallest integer greater than or equal to the number as a string"]:
|
||||||
|
|
@ -19,7 +29,16 @@ def ceil(
|
||||||
return str(math.ceil(Decimal(a)))
|
return str(math.ceil(Decimal(a)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def floor(
|
def floor(
|
||||||
a: Annotated[str, "The number to round down as a string"],
|
a: Annotated[str, "The number to round down as a string"],
|
||||||
) -> Annotated[str, "The largest integer less than or equal to the number as a string"]:
|
) -> Annotated[str, "The largest integer less than or equal to the number as a string"]:
|
||||||
|
|
@ -30,7 +49,16 @@ def floor(
|
||||||
return str(math.floor(Decimal(a)))
|
return str(math.floor(Decimal(a)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def round_num(
|
def round_num(
|
||||||
value: Annotated[str, "The number to round as a string"],
|
value: Annotated[str, "The number to round as a string"],
|
||||||
ndigits: Annotated[str, "The number of digits after the decimal point as a string"],
|
ndigits: Annotated[str, "The number of digits after the decimal point as a string"],
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,22 @@ from decimal import Decimal
|
||||||
from statistics import median as stats_median
|
from statistics import median as stats_median
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def avg(
|
def avg(
|
||||||
numbers: Annotated[list[str], "The list of numbers as strings"],
|
numbers: Annotated[list[str], "The list of numbers as strings"],
|
||||||
) -> Annotated[str, "The average (mean) of the numbers in the list as a string"]:
|
) -> Annotated[str, "The average (mean) of the numbers in the list as a string"]:
|
||||||
|
|
@ -21,7 +31,16 @@ def avg(
|
||||||
return str(sum(d_numbers) / len(d_numbers)) if d_numbers else "0.0"
|
return str(sum(d_numbers) / len(d_numbers)) if d_numbers else "0.0"
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def median(
|
def median(
|
||||||
numbers: Annotated[list[str], "A list of numbers as strings"],
|
numbers: Annotated[list[str], "A list of numbers as strings"],
|
||||||
) -> Annotated[str, "The median value of the numbers in the list as a string"]:
|
) -> Annotated[str, "The median value of the numbers in the list as a string"]:
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,22 @@ import math
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, ToolMetadata
|
||||||
from arcade_tdk import tool
|
from arcade_tdk import tool
|
||||||
|
|
||||||
decimal.getcontext().prec = 100
|
decimal.getcontext().prec = 100
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def deg_to_rad(
|
def deg_to_rad(
|
||||||
degrees: Annotated[str, "Angle in degrees as a string"],
|
degrees: Annotated[str, "Angle in degrees as a string"],
|
||||||
) -> Annotated[str, "Angle in radians as a string"]:
|
) -> Annotated[str, "Angle in radians as a string"]:
|
||||||
|
|
@ -19,7 +29,16 @@ def deg_to_rad(
|
||||||
return str(math.radians(Decimal(degrees)))
|
return str(math.radians(Decimal(degrees)))
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool(
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
def rad_to_deg(
|
def rad_to_deg(
|
||||||
radians: Annotated[str, "Angle in radians as a string"],
|
radians: Annotated[str, "Angle in radians as a string"],
|
||||||
) -> Annotated[str, "Angle in degrees as a string"]:
|
) -> Annotated[str, "Angle in degrees as a string"]:
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_math"
|
name = "arcade_math"
|
||||||
version = "1.0.5"
|
version = "1.1.0"
|
||||||
description = "Arcade.dev LLM tools for doing math"
|
description = "Arcade.dev LLM tools for doing math"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
]
|
]
|
||||||
[[project.authors]]
|
[[project.authors]]
|
||||||
name = "Arcade"
|
name = "Arcade"
|
||||||
|
|
@ -33,6 +34,7 @@ dev = [
|
||||||
arcade-mcp = {path = "../../", editable = true}
|
arcade-mcp = {path = "../../", editable = true}
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
files = [ "arcade_math/**/*.py",]
|
files = [ "arcade_math/**/*.py",]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, Operation, ToolMetadata
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.errors import RetryableToolError
|
from arcade_tdk.errors import RetryableToolError
|
||||||
|
|
||||||
|
|
@ -20,7 +21,18 @@ from .utils import (
|
||||||
# BANNED = "banned"
|
# BANNED = "banned"
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_databases(
|
async def discover_databases(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
|
|
@ -32,7 +44,18 @@ async def discover_databases(
|
||||||
return databases
|
return databases
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_collections(
|
async def discover_collections(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
database_name: Annotated[str, "The database name to discover collections in"],
|
database_name: Annotated[str, "The database name to discover collections in"],
|
||||||
|
|
@ -48,7 +71,18 @@ async def discover_collections(
|
||||||
return list(collections)
|
return list(collections)
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def get_collection_schema(
|
async def get_collection_schema(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
database_name: Annotated[str, "The database name to get the collection schema of"],
|
database_name: Annotated[str, "The database name to get the collection schema of"],
|
||||||
|
|
@ -90,7 +124,18 @@ async def get_collection_schema(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def find_documents(
|
async def find_documents(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
database_name: Annotated[str, "The database name to query"],
|
database_name: Annotated[str, "The database name to query"],
|
||||||
|
|
@ -193,7 +238,18 @@ async def find_documents(
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def count_documents(
|
async def count_documents(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
database_name: Annotated[str, "The database name to query"],
|
database_name: Annotated[str, "The database name to query"],
|
||||||
|
|
@ -230,7 +286,18 @@ async def count_documents(
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["MONGODB_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["MONGODB_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def aggregate_documents(
|
async def aggregate_documents(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
database_name: Annotated[str, "The database name to query"],
|
database_name: Annotated[str, "The database name to query"],
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_mongodb"
|
name = "arcade_mongodb"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "Tools to query and explore a MongoDB database"
|
description = "Tools to query and explore a MongoDB database"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"pymongo>=4.10.1",
|
"pymongo>=4.10.1",
|
||||||
"pydantic>=2.11.7",
|
"pydantic>=2.11.7",
|
||||||
"motor>=3.6.0",
|
"motor>=3.6.0",
|
||||||
|
|
@ -37,6 +38,7 @@ dev = [
|
||||||
arcade-mcp = { path = "../../", editable = true }
|
arcade-mcp = { path = "../../", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import Behavior, Operation, ToolMetadata
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.errors import RetryableToolError
|
from arcade_tdk.errors import RetryableToolError
|
||||||
from sqlalchemy import inspect, text
|
from sqlalchemy import inspect, text
|
||||||
|
|
@ -8,7 +9,18 @@ from sqlalchemy.ext.asyncio import AsyncEngine
|
||||||
from ..database_engine import MAX_ROWS_RETURNED, DatabaseEngine
|
from ..database_engine import MAX_ROWS_RETURNED, DatabaseEngine
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_schemas(
|
async def discover_schemas(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
|
|
@ -20,7 +32,18 @@ async def discover_schemas(
|
||||||
return schemas
|
return schemas
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def discover_tables(
|
async def discover_tables(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
schema_name: Annotated[
|
schema_name: Annotated[
|
||||||
|
|
@ -38,7 +61,18 @@ async def discover_tables(
|
||||||
return tables
|
return tables
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def get_table_schema(
|
async def get_table_schema(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
schema_name: Annotated[str, "The database schema to get the table schema of"],
|
schema_name: Annotated[str, "The database schema to get the table schema of"],
|
||||||
|
|
@ -55,7 +89,18 @@ async def get_table_schema(
|
||||||
return await _get_table_schema(engine, schema_name, table_name)
|
return await _get_table_schema(engine, schema_name, table_name)
|
||||||
|
|
||||||
|
|
||||||
@tool(requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"])
|
@tool(
|
||||||
|
requires_secrets=["POSTGRES_DATABASE_CONNECTION_STRING"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
async def execute_select_query(
|
async def execute_select_query(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
select_clause: Annotated[
|
select_clause: Annotated[
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_postgres"
|
name = "arcade_postgres"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
description = "Tools to query and explore a postgres database"
|
description = "Tools to query and explore a postgres database"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"psycopg2-binary>=2.9.10",
|
"psycopg2-binary>=2.9.10",
|
||||||
"pydantic>=2.11.7",
|
"pydantic>=2.11.7",
|
||||||
"sqlalchemy>=2.0.41",
|
"sqlalchemy>=2.0.41",
|
||||||
|
|
@ -40,6 +41,7 @@ dev = [
|
||||||
arcade-mcp = { path = "../../", editable = true }
|
arcade-mcp = { path = "../../", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ from arcade_zendesk.tools import (
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"list_tickets",
|
|
||||||
"add_ticket_comment",
|
"add_ticket_comment",
|
||||||
"get_ticket_comments",
|
"get_ticket_comments",
|
||||||
|
"list_tickets",
|
||||||
"mark_ticket_solved",
|
"mark_ticket_solved",
|
||||||
"search_articles",
|
"search_articles",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ from arcade_zendesk.tools.tickets import (
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"list_tickets",
|
|
||||||
"add_ticket_comment",
|
"add_ticket_comment",
|
||||||
"get_ticket_comments",
|
"get_ticket_comments",
|
||||||
|
"list_tickets",
|
||||||
"mark_ticket_solved",
|
"mark_ticket_solved",
|
||||||
"search_articles",
|
"search_articles",
|
||||||
"who_am_i",
|
"who_am_i",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,13 @@ import logging
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from arcade_mcp_server.metadata import (
|
||||||
|
Behavior,
|
||||||
|
Classification,
|
||||||
|
Operation,
|
||||||
|
ServiceDomain,
|
||||||
|
ToolMetadata,
|
||||||
|
)
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.auth import OAuth2
|
from arcade_tdk.auth import OAuth2
|
||||||
from arcade_tdk.errors import RetryableToolError
|
from arcade_tdk.errors import RetryableToolError
|
||||||
|
|
@ -20,6 +27,18 @@ logger = logging.getLogger(__name__)
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def search_articles(
|
async def search_articles(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from arcade_mcp_server.metadata import (
|
||||||
|
Behavior,
|
||||||
|
Classification,
|
||||||
|
Operation,
|
||||||
|
ServiceDomain,
|
||||||
|
ToolMetadata,
|
||||||
|
)
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.auth import OAuth2
|
from arcade_tdk.auth import OAuth2
|
||||||
|
|
||||||
|
|
@ -9,6 +16,18 @@ from arcade_zendesk.who_am_i_util import build_who_am_i_response
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def who_am_i(
|
async def who_am_i(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from arcade_mcp_server.metadata import (
|
||||||
|
Behavior,
|
||||||
|
Classification,
|
||||||
|
Operation,
|
||||||
|
ServiceDomain,
|
||||||
|
ToolMetadata,
|
||||||
|
)
|
||||||
from arcade_tdk import ToolContext, tool
|
from arcade_tdk import ToolContext, tool
|
||||||
from arcade_tdk.auth import OAuth2
|
from arcade_tdk.auth import OAuth2
|
||||||
from arcade_tdk.errors import RetryableToolError
|
from arcade_tdk.errors import RetryableToolError
|
||||||
|
|
@ -23,6 +30,18 @@ def _handle_ticket_not_found(response: httpx.Response, ticket_id: int) -> None:
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def list_tickets(
|
async def list_tickets(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
@ -133,6 +152,18 @@ async def list_tickets(
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["read"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.READ],
|
||||||
|
read_only=True,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=True,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def get_ticket_comments(
|
async def get_ticket_comments(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
@ -184,6 +215,18 @@ async def get_ticket_comments(
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["tickets:write"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["tickets:write"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.CREATE],
|
||||||
|
read_only=False,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def add_ticket_comment(
|
async def add_ticket_comment(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
@ -243,6 +286,18 @@ async def add_ticket_comment(
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=OAuth2(id="zendesk", scopes=["tickets:write"]),
|
requires_auth=OAuth2(id="zendesk", scopes=["tickets:write"]),
|
||||||
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
requires_secrets=["ZENDESK_SUBDOMAIN"],
|
||||||
|
metadata=ToolMetadata(
|
||||||
|
classification=Classification(
|
||||||
|
service_domains=[ServiceDomain.CUSTOMER_SUPPORT],
|
||||||
|
),
|
||||||
|
behavior=Behavior(
|
||||||
|
operations=[Operation.UPDATE, Operation.CREATE],
|
||||||
|
read_only=False,
|
||||||
|
destructive=False,
|
||||||
|
idempotent=False,
|
||||||
|
open_world=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def mark_ticket_solved(
|
async def mark_ticket_solved(
|
||||||
context: ToolContext,
|
context: ToolContext,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcade_zendesk"
|
name = "arcade_zendesk"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade-tdk>=3.0.0,<4.0.0",
|
"arcade-tdk>=3.0.0,<4.0.0",
|
||||||
|
"arcade-mcp-server>=1.17.0,<2.0.0",
|
||||||
"httpx>=0.25.0,<1.0.0",
|
"httpx>=0.25.0,<1.0.0",
|
||||||
"beautifulsoup4>=4.0.0,<5"
|
"beautifulsoup4>=4.0.0,<5"
|
||||||
]
|
]
|
||||||
|
|
@ -32,6 +33,7 @@ dev = [
|
||||||
arcade-mcp = { path = "../../", editable = true }
|
arcade-mcp = { path = "../../", editable = true }
|
||||||
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
arcade-serve = { path = "../../libs/arcade-serve/", editable = true }
|
||||||
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
arcade-tdk = { path = "../../libs/arcade-tdk/", editable = true }
|
||||||
|
arcade-mcp-server = { path = "../../libs/arcade-mcp-server/", editable = true }
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue