Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and

### Added

- `figma` integration — 3 actions, auth: oauth2. Design collaboration
platform for creating, sharing, and commenting on design files via the
Figma REST API (list_comments, delete_comment, post_a_comment).
Producer-staged by integration-drafts; consumer-side audit applied
5 patches before merge.
- `postgrid` integration — 3 actions, auth: api_key. Programmatic direct
mail delivery via the PostGrid Print & Mail API (create_contact,
create_letter, create_postcard). Producer-staged by integration-drafts;
consumer-side audit applied 1 patch before merge.
- `canvas` integration — 5 actions, auth: custom. Learning management
system for course, assignment, and user management via the Canvas REST
API (list_accounts, list_assignments, list_courses,
Expand Down
14 changes: 10 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ slack = "modulex_integrations.tools.slack"
etsy = "modulex_integrations.tools.etsy"
exa = "modulex_integrations.tools.exa"
fal_ai = "modulex_integrations.tools.fal_ai"
figma = "modulex_integrations.tools.figma"
tavily = "modulex_integrations.tools.tavily"
insightly = "modulex_integrations.tools.insightly"
instacart = "modulex_integrations.tools.instacart"
Expand Down Expand Up @@ -102,7 +103,7 @@ jira = "modulex_integrations.tools.jira"
cal_com = "modulex_integrations.tools.cal_com"
calendly = "modulex_integrations.tools.calendly"
canva = "modulex_integrations.tools.canva"
canvas = "modulex_integrations.tools.canvas"
instructure_canvas = "modulex_integrations.tools.instructure_canvas"
linear = "modulex_integrations.tools.linear"
linkedin = "modulex_integrations.tools.linkedin"
luma = "modulex_integrations.tools.luma"
Expand Down Expand Up @@ -175,6 +176,7 @@ microsoft_teams = "modulex_integrations.tools.microsoft_teams"
mintlify = "modulex_integrations.tools.mintlify"
mixpanel = "modulex_integrations.tools.mixpanel"
monday = "modulex_integrations.tools.monday"
postgrid = "modulex_integrations.tools.postgrid"
posthog = "modulex_integrations.tools.posthog"
postman = "modulex_integrations.tools.postman"
product_hunt = "modulex_integrations.tools.product_hunt"
Expand Down Expand Up @@ -572,10 +574,14 @@ select = ["E", "F", "I", "N", "W", "B", "C4", "UP", "RUF"]
# ParameterDef / Field kwargs that cannot be wrapped.
"src/modulex_integrations/tools/shopify_partner/manifest.py" = ["E501"]
"src/modulex_integrations/tools/shopify_partner/tools.py" = ["E501"]
# canvas manifest and tools have long description string literals in
# instructure_canvas manifest and tools have long description string literals in
# ParameterDef / Field kwargs that cannot be wrapped.
"src/modulex_integrations/tools/canvas/manifest.py" = ["E501"]
"src/modulex_integrations/tools/canvas/tools.py" = ["E501"]
"src/modulex_integrations/tools/instructure_canvas/manifest.py" = ["E501"]
"src/modulex_integrations/tools/instructure_canvas/tools.py" = ["E501"]
# postgrid manifest and tools have long description string literals in
# ParameterDef / Field kwargs that cannot be wrapped.
"src/modulex_integrations/tools/postgrid/manifest.py" = ["E501"]
"src/modulex_integrations/tools/postgrid/tools.py" = ["E501"]

[tool.mypy]
python_version = "3.12"
Expand Down
2 changes: 1 addition & 1 deletion src/modulex_integrations/tools/azure_storage/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
description="Manage blobs and containers in Microsoft Azure Blob Storage",
version="1.0.0",
author="ModuleX",
logo="modulex:azure_storage-themed",
logo="modulex:azure_storage",
app_url="https://azure.microsoft.com/en-us/products/storage/blobs",
categories=["Cloud Infrastructure", "Storage"],
actions=[
Expand Down
2 changes: 1 addition & 1 deletion src/modulex_integrations/tools/browserbase/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
description="Cloud browser infrastructure for running and managing headless browser sessions",
version="1.0.0",
author="ModuleX",
logo="modulex:browserbase-themed",
logo="modulex:browserbase",
app_url="https://www.browserbase.com",
categories=["Developer Tools & Infrastructure", "automation", "browser"],
actions=[
Expand Down
2 changes: 1 addition & 1 deletion src/modulex_integrations/tools/datadog/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
description="Infrastructure monitoring, log management, and application performance platform",
version="1.0.0",
author="ModuleX",
logo="modulex:datadog-themed",
logo="logos:datadog-icon",
app_url="https://www.datadoghq.com",
categories=["Monitoring & Observability", "Developer Tools & Infrastructure"],
actions=[
Expand Down
34 changes: 34 additions & 0 deletions src/modulex_integrations/tools/figma/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Figma

Design collaboration platform for creating, sharing, and commenting on design files via the Figma REST API (`api.figma.com`).

## Authentication

### OAuth2 Authentication

- Register an OAuth app at <https://www.figma.com/developers/apps>.
- Redirect URI: `https://api.modulex.dev/credentials/oauth2/callback`
- Scopes requested: `files:read`, `file_comments:write`
- Required env vars (only when bringing your own OAuth app):
- `FIGMA_OAUTH2_CLIENT_ID` — your Figma OAuth App Client ID
- `FIGMA_OAUTH2_CLIENT_SECRET` — your Figma OAuth App Client Secret

## Tools

| name | description | required params |
| --- | --- | --- |
| `list_comments` | List all comments left on a Figma file | `file_id` |
| `delete_comment` | Delete a comment from a Figma file | `file_id`, `comment_id` |
| `post_a_comment` | Post a comment to a Figma file | `file_id`, `message` |

Every tool takes an additional `auth_type`/`auth_data` pair that the runtime fills in from the resolved OAuth2 credential.

## Limits & Quotas

- Figma REST API rate limit: 30 requests per minute per OAuth token (may vary by endpoint and plan).
- No per-request billing; API access is included with Figma Professional and above.
- Error model: non-2xx responses are caught and returned as `success=False` + `error` rather than raising.

## Maintainer

ModuleX core team.
21 changes: 21 additions & 0 deletions src/modulex_integrations/tools/figma/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Figma integration — discovered via the ``modulex.tools`` entry point."""
from modulex_integrations.tools.figma.manifest import manifest
from modulex_integrations.tools.figma.tools import (
delete_comment,
list_comments,
post_a_comment,
)

TOOLS = (
list_comments,
delete_comment,
post_a_comment,
)

__all__ = [
"TOOLS",
"delete_comment",
"list_comments",
"manifest",
"post_a_comment",
]
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Runtime dependencies for the canvas integration.
# Runtime dependencies for the figma integration.
# CI assembles this into the root pyproject's [project.optional-dependencies].
dependencies = []
121 changes: 121 additions & 0 deletions src/modulex_integrations/tools/figma/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Figma integration manifest."""
from __future__ import annotations

from modulex_integrations.schema import (
ActionDefinition,
EnvVar,
IntegrationManifest,
OAuth2AuthSchema,
OAuthConfig,
ParameterDef,
SuccessIndicators,
TestEndpoint,
)

__all__ = ["manifest"]


manifest = IntegrationManifest(
name="figma",
display_name="Figma",
description=(
"Design collaboration platform for creating, sharing,"
" and commenting on design files"
),
version="1.0.0",
author="ModuleX",
logo="logos:figma",
app_url="https://www.figma.com",
categories=["Design", "Productivity & Collaboration"],
actions=[
ActionDefinition(
name="list_comments",
description="List all comments left on a Figma file",
parameters={
"file_id": ParameterDef(
type="string",
description="The Figma file ID (found in the file URL after /file/)",
required=True,
),
},
),
ActionDefinition(
name="delete_comment",
description="Delete a comment from a Figma file",
parameters={
"file_id": ParameterDef(
type="string",
description="The Figma file ID (found in the file URL after /file/)",
required=True,
),
"comment_id": ParameterDef(
type="string",
description="The ID of the comment to delete",
required=True,
),
},
),
ActionDefinition(
name="post_a_comment",
description="Post a comment to a Figma file",
parameters={
"file_id": ParameterDef(
type="string",
description="The Figma file ID (found in the file URL after /file/)",
required=True,
),
"message": ParameterDef(
type="string",
description="The text contents of the comment to post",
required=True,
),
"comment_id": ParameterDef(
type="string",
description="The ID of the comment to reply to (must be a root comment)",
),
},
),
],
auth_schemas=[
OAuth2AuthSchema(
display_name="OAuth2 Authentication",
description="Connect using Figma OAuth (recommended)",
setup_environment_variables=[
EnvVar(
name="FIGMA_OAUTH2_CLIENT_ID",
display_name="Client ID",
description="Figma OAuth App Client ID",
required=True,
sensitive=False,
only_for_custom=True,
about_url="https://www.figma.com/developers/apps",
),
EnvVar(
name="FIGMA_OAUTH2_CLIENT_SECRET",
display_name="Client Secret",
description="Figma OAuth App Client Secret",
required=True,
sensitive=True,
only_for_custom=True,
about_url="https://www.figma.com/developers/apps",
),
],
oauth_config=OAuthConfig(
auth_url="https://www.figma.com/oauth",
token_url="https://api.figma.com/v1/oauth/token",
scopes=["files:read", "file_comments:write"],
),
test_endpoint=TestEndpoint(
url="https://api.figma.com/v1/me",
method="GET",
headers={"Authorization": "Bearer {access_token}"},
success_indicators=SuccessIndicators(
status_codes=[200],
response_fields=["id"],
),
cost_level="free",
description="Validates OAuth token by fetching authenticated user info",
),
),
],
)
62 changes: 62 additions & 0 deletions src/modulex_integrations/tools/figma/outputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Pydantic response models for the figma integration's @tool functions."""
from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field

__all__ = [
"CommentUser",
"DeleteCommentOutput",
"FigmaComment",
"ListCommentsOutput",
"PostACommentOutput",
]


class _Base(BaseModel):
"""Shared config for every output model in this integration."""

model_config = ConfigDict(extra="forbid")


# --- Nested resource models -----------------------------------------------


class CommentUser(_Base):
"""A Figma user who authored a comment."""

handle: str | None = None
img_url: str | None = None
id: str | None = None


class FigmaComment(_Base):
"""A single comment on a Figma file."""

id: str | None = None
file_key: str | None = None
parent_id: str | None = None
user: CommentUser | None = None
created_at: str | None = None
resolved_at: str | None = None
message: str | None = None
order_id: str | None = None


# --- Per-action output models ---------------------------------------------


class ListCommentsOutput(_Base):
success: bool
error: str | None = None
comments: list[FigmaComment] = Field(default_factory=list)


class DeleteCommentOutput(_Base):
success: bool
error: str | None = None


class PostACommentOutput(_Base):
success: bool
error: str | None = None
comment: FigmaComment | None = None
Loading
Loading