diff --git a/packages/mcp/src/pipefy_mcp/tools/field_condition_tools.py b/packages/mcp/src/pipefy_mcp/tools/field_condition_tools.py index f1c0a98f..32883386 100644 --- a/packages/mcp/src/pipefy_mcp/tools/field_condition_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/field_condition_tools.py @@ -18,6 +18,7 @@ field_condition_actions_error_message, handle_pipe_config_tool_graphql_error, ) +from pipefy_mcp.tools.remote_profile import REMOTE from pipefy_mcp.tools.tool_context import get_pipefy_client from pipefy_mcp.tools.validation_helpers import ( validate_tool_id, @@ -48,6 +49,7 @@ def register(mcp: FastMCP) -> None: annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_field_conditions( ctx: Context[ServerSession, None], @@ -104,6 +106,7 @@ async def get_field_conditions( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_field_condition( ctx: Context[ServerSession, None], diff --git a/packages/mcp/src/pipefy_mcp/tools/pipe_config_tools.py b/packages/mcp/src/pipefy_mcp/tools/pipe_config_tools.py index 84feabf4..dd8ca808 100644 --- a/packages/mcp/src/pipefy_mcp/tools/pipe_config_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/pipe_config_tools.py @@ -370,6 +370,7 @@ async def clone_pipe( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_phase_allowed_move_targets( phase_id: PipefyId, @@ -423,6 +424,7 @@ async def get_phase_allowed_move_targets( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_phase_cards_count( phase_id: PipefyId, diff --git a/packages/mcp/src/pipefy_mcp/tools/pipe_tools.py b/packages/mcp/src/pipefy_mcp/tools/pipe_tools.py index e594728c..2fcc5fb6 100644 --- a/packages/mcp/src/pipefy_mcp/tools/pipe_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/pipe_tools.py @@ -326,6 +326,7 @@ async def get_card( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_card_relations( ctx: Context[ServerSession, None], @@ -774,6 +775,7 @@ async def get_pipe( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_labels( ctx: Context[ServerSession, None], @@ -827,6 +829,7 @@ async def get_labels( annotations=ToolAnnotations( readOnlyHint=True, ), + meta=REMOTE, ) async def get_pipe_members( ctx: Context[ServerSession, None], diff --git a/packages/mcp/src/pipefy_mcp/tools/portal_tools.py b/packages/mcp/src/pipefy_mcp/tools/portal_tools.py index c292b74f..e6210c01 100644 --- a/packages/mcp/src/pipefy_mcp/tools/portal_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/portal_tools.py @@ -32,6 +32,7 @@ validate_sort_page_ids_no_duplicates, validate_tool_ids, ) +from pipefy_mcp.tools.remote_profile import REMOTE from pipefy_mcp.tools.tool_context import get_pipefy_client from pipefy_mcp.tools.validation_helpers import validate_tool_id @@ -45,6 +46,7 @@ def register(mcp: FastMCP) -> None: @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def list_portals( ctx: Context[ServerSession, None], @@ -84,6 +86,7 @@ async def list_portals( @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_portal( ctx: Context[ServerSession, None], diff --git a/packages/mcp/src/pipefy_mcp/tools/relation_tools.py b/packages/mcp/src/pipefy_mcp/tools/relation_tools.py index 51c26350..bce7ead9 100644 --- a/packages/mcp/src/pipefy_mcp/tools/relation_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/relation_tools.py @@ -16,6 +16,7 @@ build_relation_read_success_payload, handle_relation_tool_graphql_error, ) +from pipefy_mcp.tools.remote_profile import REMOTE from pipefy_mcp.tools.tool_context import get_pipefy_client from pipefy_mcp.tools.validation_helpers import ( mutation_error_if_not_optional_dict, @@ -30,6 +31,7 @@ class RelationTools: def register(mcp: FastMCP) -> None: @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_pipe_relations(pipe_id: PipefyId, ctx: Context) -> dict[str, Any]: """List pipe relations for a pipe (parent and child links, config, and repo refs). @@ -61,6 +63,7 @@ async def get_pipe_relations(pipe_id: PipefyId, ctx: Context) -> dict[str, Any]: @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_table_relations( relation_ids: list[PipefyId], ctx: Context diff --git a/packages/mcp/src/pipefy_mcp/tools/webhook_tools.py b/packages/mcp/src/pipefy_mcp/tools/webhook_tools.py index d97ba77c..94236bb5 100644 --- a/packages/mcp/src/pipefy_mcp/tools/webhook_tools.py +++ b/packages/mcp/src/pipefy_mcp/tools/webhook_tools.py @@ -10,6 +10,7 @@ from pipefy_sdk import PipefyId from pipefy_mcp.tools.destructive_tool_guard import check_destructive_confirmation +from pipefy_mcp.tools.remote_profile import REMOTE from pipefy_mcp.tools.tool_context import get_pipefy_client from pipefy_mcp.tools.validation_helpers import ( mutation_error_if_not_optional_dict, @@ -29,6 +30,7 @@ class WebhookTools: def register(mcp: FastMCP) -> None: @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_email_templates( repo_id: PipefyId, @@ -73,6 +75,7 @@ async def get_email_templates( @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_card_inbox_emails( card_id: PipefyId, @@ -268,6 +271,7 @@ async def send_email_with_template( @mcp.tool( annotations=ToolAnnotations(readOnlyHint=True), + meta=REMOTE, ) async def get_webhooks( ctx: Context[ServerSession, None], diff --git a/packages/mcp/tests/tools/test_remote_profile.py b/packages/mcp/tests/tools/test_remote_profile.py index 800ce458..e0ae08a7 100644 --- a/packages/mcp/tests/tools/test_remote_profile.py +++ b/packages/mcp/tests/tools/test_remote_profile.py @@ -92,6 +92,26 @@ "get_pipe_report_columns", "get_pipe_report_filterable_fields", "get_pipe_report_export", + # pipe / card / field / member / webhook / portal reads (#441): read tools + # that reach the API with the request-scoped bearer and are governed by API + # permissions; no filesystem or per-user process-global settings reads. The + # relation reads use the public GraphQL API only and the portal reads use + # the Interfaces schema; Pipefy's Internal API is reached only by mutations + # such as delete_card_relation, which stay withheld. + "get_card_relations", + "get_card_inbox_emails", + "get_field_condition", + "get_field_conditions", + "get_labels", + "get_pipe_members", + "get_pipe_relations", + "get_phase_allowed_move_targets", + "get_phase_cards_count", + "get_table_relations", + "get_webhooks", + "get_email_templates", + "get_portal", + "list_portals", } )