pipefy-mcp-server 0.3.0a1__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.
- pipefy_mcp_server-0.3.0a1/.gitignore +56 -0
- pipefy_mcp_server-0.3.0a1/AGENTS.md +169 -0
- pipefy_mcp_server-0.3.0a1/CLAUDE.md +1 -0
- pipefy_mcp_server-0.3.0a1/PKG-INFO +141 -0
- pipefy_mcp_server-0.3.0a1/README.md +127 -0
- pipefy_mcp_server-0.3.0a1/pyproject.toml +69 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/__init__.py +16 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/_docs.py +7 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/auth/__init__.py +19 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/auth/request_identity.py +94 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/auth/resource_server.py +175 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/core/fastmcp_tool_lifecycle.py +46 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/core/runtime.py +164 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/main.py +100 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/server.py +178 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/settings.py +288 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/__init__.py +5 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/ai_agent_tools.py +649 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/ai_automation_tools.py +435 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/ai_tool_helpers.py +370 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/attachment_tool_helpers.py +137 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/attachment_tools.py +219 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/automation_tool_helpers.py +194 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/automation_tools.py +676 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/behavior_placeholder_interpolation.py +23 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/destructive_tool_guard.py +100 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/field_condition_tools.py +482 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/graphql_error_helpers.py +590 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/introspection_tool_helpers.py +53 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/introspection_tools.py +190 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/mcp_capabilities.py +36 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/member_tool_helpers.py +86 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/member_tools.py +247 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/observability_tool_helpers.py +101 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/observability_tools.py +535 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/organization_tools.py +43 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/pagination_helpers.py +94 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/phase_transition_helpers.py +84 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/pipe_config_tool_helpers.py +629 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/pipe_config_tools.py +1297 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/pipe_tool_helpers.py +521 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/pipe_tools.py +1336 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/portal_tool_helpers.py +264 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/portal_tools.py +1032 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/registry.py +284 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/relation_tool_helpers.py +101 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/relation_tools.py +328 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/remote_profile.py +31 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/report_tool_helpers.py +103 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/report_tools.py +857 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/table_tool_helpers.py +243 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/table_tools.py +1060 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/tool_context.py +23 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/tool_error_envelope.py +132 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/transition_hints.py +35 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/validation_envelope.py +179 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/validation_helpers.py +130 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/webhook_tool_helpers.py +72 -0
- pipefy_mcp_server-0.3.0a1/src/pipefy_mcp/tools/webhook_tools.py +510 -0
- pipefy_mcp_server-0.3.0a1/tests/_rs_fixtures.py +30 -0
- pipefy_mcp_server-0.3.0a1/tests/auth/__init__.py +0 -0
- pipefy_mcp_server-0.3.0a1/tests/auth/test_request_identity.py +183 -0
- pipefy_mcp_server-0.3.0a1/tests/auth/test_resource_server.py +244 -0
- pipefy_mcp_server-0.3.0a1/tests/conftest.py +32 -0
- pipefy_mcp_server-0.3.0a1/tests/core/test_runtime.py +198 -0
- pipefy_mcp_server-0.3.0a1/tests/test_main.py +177 -0
- pipefy_mcp_server-0.3.0a1/tests/test_server.py +429 -0
- pipefy_mcp_server-0.3.0a1/tests/test_settings.py +203 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/__init__.py +0 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/conftest.py +115 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_ai_agent_tools.py +2355 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_ai_automation_tools.py +1494 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_ai_automation_tools_live.py +105 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_ai_tool_helpers.py +772 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_ai_tool_helpers_slug_resolution.py +546 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_attachment_tool_helpers.py +127 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_attachment_tools.py +640 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_attachment_tools_live.py +230 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_automation_tools.py +1117 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_behavior_placeholder_interpolation.py +323 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_destructive_preview_dependents.py +124 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_destructive_tool_guard.py +111 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_docstring_discovery_hints.py +174 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_field_condition_tools.py +203 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_field_conditions_tools_live.py +154 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_graphql_error_enrichment.py +306 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_graphql_error_helpers.py +158 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_introspection_scenarios.py +173 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_introspection_tools.py +407 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_introspection_tools_live.py +111 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_mcp_capabilities.py +47 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_member_tools.py +498 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_observability_tools.py +1344 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_organization_tools.py +124 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pagination_helpers.py +111 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_phase_transition_helpers.py +403 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_config_mcp_signoff_live.py +190 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_config_tool_helpers.py +171 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_config_tools.py +2816 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_config_tools_live.py +142 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_tool_helpers.py +684 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_pipe_tools.py +2900 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_portal_tool_helpers.py +86 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_portal_tools.py +2343 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_registry.py +172 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_relation_tools.py +432 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_remote_profile.py +124 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_report_tool_helpers.py +60 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_report_tools.py +1752 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_table_tools.py +1737 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_tool_context.py +50 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_tool_error_envelope.py +22 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_tool_success_envelope.py +33 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_validation_envelope.py +158 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_validation_helpers.py +127 -0
- pipefy_mcp_server-0.3.0a1/tests/tools/test_webhook_tools.py +1096 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Ruff stuff:
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
|
|
12
|
+
# Unit test / coverage reports
|
|
13
|
+
htmlcov/
|
|
14
|
+
.tox/
|
|
15
|
+
.nox/
|
|
16
|
+
.coverage
|
|
17
|
+
.coverage.*
|
|
18
|
+
.cache
|
|
19
|
+
nosetests.xml
|
|
20
|
+
coverage.xml
|
|
21
|
+
*.cover
|
|
22
|
+
*.py,cover
|
|
23
|
+
.hypothesis/
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
cover/
|
|
26
|
+
|
|
27
|
+
# Environments
|
|
28
|
+
.env
|
|
29
|
+
.venv
|
|
30
|
+
env/
|
|
31
|
+
venv/
|
|
32
|
+
ENV/
|
|
33
|
+
env.bak/
|
|
34
|
+
venv.bak/
|
|
35
|
+
.mise.toml
|
|
36
|
+
|
|
37
|
+
# Ralph Wiggum (local autonomous runs; state must not land in git)
|
|
38
|
+
.ralph/
|
|
39
|
+
RALPH_TASK.md
|
|
40
|
+
|
|
41
|
+
# IDEs and editors
|
|
42
|
+
.cursor/
|
|
43
|
+
.claude/
|
|
44
|
+
# Cursor MCP workspace config (machine-specific paths or local server entries).
|
|
45
|
+
# Carve-out: repo-root .mcp.json is the Claude Code plugin's MCP server config
|
|
46
|
+
# (auto-discovered at plugin root) and is intentionally tracked.
|
|
47
|
+
.mcp.json
|
|
48
|
+
!/.mcp.json
|
|
49
|
+
|
|
50
|
+
# Tracked release tooling only (ignore ad-hoc local scripts and smoke artifacts)
|
|
51
|
+
scripts/*
|
|
52
|
+
!scripts/bump_version.py
|
|
53
|
+
|
|
54
|
+
# macOS
|
|
55
|
+
.DS_Store
|
|
56
|
+
**/.DS_Store
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# MCP package conventions
|
|
2
|
+
|
|
3
|
+
Scoped to `packages/mcp/`. Repo-wide guidance lives in `../../AGENTS.md`.
|
|
4
|
+
|
|
5
|
+
## Distribution model
|
|
6
|
+
|
|
7
|
+
This MCP server is distributed as code that runs in the user's environment as
|
|
8
|
+
a subprocess of the agent runtime (Claude Code, Claude Desktop, etc.). The
|
|
9
|
+
trust boundary is the user; the server has the same filesystem and network
|
|
10
|
+
access the user already has.
|
|
11
|
+
|
|
12
|
+
Implications for tool design:
|
|
13
|
+
|
|
14
|
+
- Local filesystem inputs (`file_path`) are first-class. There is no
|
|
15
|
+
path-traversal threat surface beyond what the user can already access.
|
|
16
|
+
- SSRF guards, redirect loops, and download size caps that defend a hosted
|
|
17
|
+
server are not appropriate here. They add maintenance cost without buying
|
|
18
|
+
a security boundary.
|
|
19
|
+
|
|
20
|
+
A hosted/remote distribution profile is in progress. It runs the server as a
|
|
21
|
+
multi-user HTTP service. Tool exposure there is **default-deny**: only tools
|
|
22
|
+
explicitly marked remote-safe are registered; everything else is withheld. The
|
|
23
|
+
marker is described below.
|
|
24
|
+
|
|
25
|
+
## Transport profiles
|
|
26
|
+
|
|
27
|
+
`pipefy-mcp-server` launches with two orthogonal flags:
|
|
28
|
+
|
|
29
|
+
- **`--profile {local|remote}`** (default `local`, env `PIPEFY_MCP_PROFILE`).
|
|
30
|
+
- `local`: registers every tool and acts as the one credential resolved at
|
|
31
|
+
startup. The installed-subprocess case.
|
|
32
|
+
- `remote`: exposes ONLY the default-deny remote-safe tool surface and, when a
|
|
33
|
+
resource-server URL is configured, validates an inbound bearer per request.
|
|
34
|
+
- **`--transport {stdio|http}`** (env `PIPEFY_MCP_TRANSPORT`). Left unset it follows
|
|
35
|
+
the profile: `local` speaks stdio, `remote` serves over Streamable HTTP. Set it
|
|
36
|
+
explicitly to run `local` over loopback HTTP. `remote` over stdio is rejected: a
|
|
37
|
+
per-request bearer has no stdio equivalent. The pair is resolved (and validated)
|
|
38
|
+
once, at startup, by `resolve_mcp_settings`.
|
|
39
|
+
|
|
40
|
+
Bind host/port come from `PIPEFY_MCP_HOST` / `PIPEFY_MCP_PORT` (defaults
|
|
41
|
+
`127.0.0.1:8000`), overridable with `--host` / `--port`, and matter only over HTTP.
|
|
42
|
+
|
|
43
|
+
Under `remote` the server acts on behalf of each caller: it validates the inbound
|
|
44
|
+
bearer per request and the one shared client reads that validated bearer per call,
|
|
45
|
+
so concurrent callers each act as themselves rather than as a single identity
|
|
46
|
+
resolved at startup. `local` runs as the one credential resolved at startup. The
|
|
47
|
+
transport still binds loopback-only until the DNS-rebinding host/Origin allowlist
|
|
48
|
+
lands.
|
|
49
|
+
|
|
50
|
+
**Resource-server profile.** Config is split by domain. *Token validation* is an
|
|
51
|
+
auth concern and lives in `pipefy_auth.JwtValidationSettings` (`settings.jwt`,
|
|
52
|
+
env `PIPEFY_JWT_*`): `ISSUER_URL` (an override; absent it, the inbound issuer
|
|
53
|
+
defaults to the one this process logs into, the `OidcClient` issuer, since in a
|
|
54
|
+
single-realm deployment they are the same IdP), optional `AUDIENCE` /
|
|
55
|
+
`VERIFY_AUDIENCE` (off by default, the same-audience interim), and `JWKS_URI`.
|
|
56
|
+
*Resource identity* is MCP-specific and stays in `pipefy_mcp.ResourceServerSettings`
|
|
57
|
+
(`settings.rs`, env `PIPEFY_MCP_RS_*`): `RESOURCE_SERVER_URL` (this server's public
|
|
58
|
+
canonical URL, e.g. `https://host/mcp`) and `REQUIRED_SCOPES`. The shared
|
|
59
|
+
`PIPEFY_ALLOW_INSECURE_URLS` covers both. The profile activates when
|
|
60
|
+
`RESOURCE_SERVER_URL` is set (the one value that cannot default); there is no
|
|
61
|
+
separate enable flag, just the `remote` profile plus this URL. Set
|
|
62
|
+
`RESOURCE_SERVER_URL` with the stored-session login disabled and no `ISSUER_URL`
|
|
63
|
+
override and startup fails (no issuer to validate against).
|
|
64
|
+
|
|
65
|
+
The JWKS/RS256 validation lives in `pipefy_auth` (`JwtValidator`); the MCP adapter
|
|
66
|
+
`auth/resource_server.py` (`JwtTokenVerifier`) maps validated claims onto the
|
|
67
|
+
SDK's `AccessToken`. FastMCP serves the RFC 9728 protected-resource metadata and
|
|
68
|
+
the `401` + `WWW-Authenticate` challenge; `build_resource_server_auth` (same
|
|
69
|
+
module) resolves the inbound issuer and pairs the verifier with `AuthSettings`.
|
|
70
|
+
The runtime (`McpRuntime.for_profile`) calls it for the `remote` profile and holds
|
|
71
|
+
the pair as `inbound_auth`, which `server.py` wires into the app.
|
|
72
|
+
|
|
73
|
+
**Loopback bind.** `_assert_safe_http_bind` restricts the HTTP transport to a
|
|
74
|
+
loopback bind, unconditionally for now. Per-request on-behalf-of identity (each
|
|
75
|
+
call runs as the validated caller, not a single startup identity) means inbound
|
|
76
|
+
identity is not the constraint; the constraint is DNS-rebinding protection, the
|
|
77
|
+
configurable host / Origin allowlist for a proxied deployment. Off-loopback binding
|
|
78
|
+
stays off until that lands (see `experiments/hosted-obo/RFC-OUTLINE.md`). The
|
|
79
|
+
attachment tools' local `file_path` inputs also still assume a loopback peer that
|
|
80
|
+
shares the client's disk (remote-safe file inputs are separate follow-up work).
|
|
81
|
+
|
|
82
|
+
## Tool registration
|
|
83
|
+
|
|
84
|
+
Tools are registered **once, at construction** (via `_register_pipefy_tools` in
|
|
85
|
+
`server.py`, reached through `build_pipefy_mcp_server`, which both transports use),
|
|
86
|
+
not inside the FastMCP `lifespan`. The lifespan owns resources only: it yields
|
|
87
|
+
the already-wired app-scoped runtime as the request `lifespan_context`. This
|
|
88
|
+
follows the FastMCP contract, where the lifespan can run per session (per request
|
|
89
|
+
under Streamable HTTP) and so must not mutate the tool table.
|
|
90
|
+
|
|
91
|
+
Tools take no client at registration. Each tool function declares a
|
|
92
|
+
`ctx: Context` parameter (FastMCP injects it and keeps it out of the tool's
|
|
93
|
+
input schema) and resolves the live client per request with
|
|
94
|
+
`get_pipefy_client(ctx)` (`tools/tool_context.py`), which reads
|
|
95
|
+
`ctx.request_context.lifespan_context.pipefy_client`. Because the client is
|
|
96
|
+
looked up per call rather than captured at registration, tools observe whatever
|
|
97
|
+
the runtime holds without re-registering; under the hosted profile the one shared
|
|
98
|
+
client applies each request's identity itself (via its request-context bearer), so
|
|
99
|
+
identity is per-request even though the client is stable. That is why there is no
|
|
100
|
+
repeat-visit bookkeeping: registration never repeats.
|
|
101
|
+
|
|
102
|
+
When adding a tool, give it a `ctx: Context` parameter and start its body with
|
|
103
|
+
`client = get_pipefy_client(ctx)`; do not pass a client through `register`.
|
|
104
|
+
|
|
105
|
+
Both transports launch through the single `run_server` entry point, which resolves
|
|
106
|
+
the profile/transport once (via `resolve_mcp_settings`) and builds the same app
|
|
107
|
+
through `build_pipefy_mcp_server` (same runtime-bound lifespan, same
|
|
108
|
+
`_register_pipefy_tools`), differing only in the transport `run` and HTTP's bind
|
|
109
|
+
concerns. `build_pipefy_mcp_server` constructs one app-scoped `McpRuntime` via
|
|
110
|
+
`McpRuntime.for_profile` (`core/runtime.py`) and binds the lifespan to it.
|
|
111
|
+
`for_profile` is the composition root's one build step: the `remote` profile picks
|
|
112
|
+
a per-request identity and builds the inbound resource-server `(verifier, auth)`
|
|
113
|
+
pair (failing fast when that profile has no resource server); every other profile
|
|
114
|
+
resolves the one startup credential and fails fast when none is configured
|
|
115
|
+
(`StartupIdentity.from_configured_credential`). So a missing credential (or, under
|
|
116
|
+
`remote`, a missing resource server) surfaces when the server is built at startup,
|
|
117
|
+
not on the first tool call. The runtime wires its shared client to whichever
|
|
118
|
+
identity's `httpx.Auth` it was handed and exposes the inbound pair as
|
|
119
|
+
`inbound_auth`, which `build_pipefy_mcp_server` reads into FastMCP. (This also
|
|
120
|
+
means `build_pipefy_mcp_server` resolves the credential, so the live integration
|
|
121
|
+
tests that build the app at import skip themselves when no creds are configured.) Wiring the client at construction is safe off the event
|
|
122
|
+
loop: `PipefyClient` construction does no network I/O and binds nothing to a
|
|
123
|
+
running loop, because its executors open a fresh per-request transport at call
|
|
124
|
+
time; the client built at startup works on whatever loop later serves requests.
|
|
125
|
+
Streamable HTTP re-entering the lifespan per session just yields the same
|
|
126
|
+
already-wired runtime, so there is nothing to rebuild. The runtime holds no
|
|
127
|
+
per-request state: both identity variants carry an `httpx.Auth` the shared client
|
|
128
|
+
applies to every outbound call. `StartupIdentity` carries the one credential
|
|
129
|
+
resolved at startup (stdio/local), while `RequestScopedIdentity` carries a
|
|
130
|
+
`RequestContextBearerAuth` that reads each caller's validated bearer from the
|
|
131
|
+
request context, so one client serves every concurrent caller as themselves.
|
|
132
|
+
|
|
133
|
+
## Remote-profile tool marker
|
|
134
|
+
|
|
135
|
+
Under the `remote` profile (`--profile remote` / `PIPEFY_MCP_PROFILE=remote`), the
|
|
136
|
+
server exposes ONLY tools whose registration carries `meta=REMOTE`. Any unmarked
|
|
137
|
+
tool is implicitly withheld (default-deny). Under `local` (the default), all tools
|
|
138
|
+
register and the marker is inert.
|
|
139
|
+
|
|
140
|
+
The marker is a single co-located source of truth on the `@mcp.tool` decorator:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pipefy_mcp.tools.remote_profile import REMOTE
|
|
144
|
+
|
|
145
|
+
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True), meta=REMOTE)
|
|
146
|
+
async def get_organization(...): ...
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`ToolRegistry.apply_remote_profile()` reads it back via `is_remote_tool` and
|
|
150
|
+
removes every unmarked Pipefy tool at registration time (before the server
|
|
151
|
+
serves anything). The marker is greppable (`rg "meta=REMOTE" packages/mcp`) and
|
|
152
|
+
machine-enforced, unlike the comment-only `GATED:` convention it replaces.
|
|
153
|
+
|
|
154
|
+
Inclusion criteria for marking a tool remote-safe: it reaches the API with the
|
|
155
|
+
request-scoped bearer and is fully governed by API permissions; it does NOT read
|
|
156
|
+
the local filesystem; it does NOT read process-global settings for a per-user
|
|
157
|
+
decision. Opting a tool in is a deliberate, reviewed change (it shifts the
|
|
158
|
+
`REMOTE_SEED` drift guard in `tests/tools/test_remote_profile.py`).
|
|
159
|
+
|
|
160
|
+
### Exposure vs input restriction
|
|
161
|
+
|
|
162
|
+
The `meta` marker expresses **exposure** only: whether a tool is available in the
|
|
163
|
+
remote profile. The retired `GATED:` convention could also express *input*
|
|
164
|
+
restriction within an exposed tool. Where a remotely-exposed tool needs restricted
|
|
165
|
+
inputs (for example the attachment tools, which would accept a `file_url` rather
|
|
166
|
+
than a local `file_path`), enforce that in the tool body gated on
|
|
167
|
+
`settings.mcp.profile == "remote"` at call time, not via the marker. A tool whose
|
|
168
|
+
exclusion deserves a reason gets a plain code comment stating why; the exclusion
|
|
169
|
+
itself needs no annotation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pipefy-mcp-server
|
|
3
|
+
Version: 0.3.0a1
|
|
4
|
+
Summary: MCP server that exposes Pipefy's GraphQL API.
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx>=0.27.0
|
|
7
|
+
Requires-Dist: mcp[cli]>=1.25.0
|
|
8
|
+
Requires-Dist: pipefy
|
|
9
|
+
Requires-Dist: pipefy-auth
|
|
10
|
+
Requires-Dist: pipefy-infra
|
|
11
|
+
Requires-Dist: pydantic-settings>=2.8.1
|
|
12
|
+
Requires-Dist: pydantic<3,>=2.13.4
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# pipefy-mcp-server
|
|
16
|
+
|
|
17
|
+
MCP server for Pipefy — **152 tools** for AI agents (Cursor, Claude Desktop, Claude Code, Codex, and any MCP-compatible client). Depends on [`pipefy`](../sdk/README.md) for all GraphQL and API logic.
|
|
18
|
+
|
|
19
|
+
## Install (pre-launch, v0.1 → v0.5)
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
uvx \
|
|
23
|
+
--with "pipefy @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/sdk" \
|
|
24
|
+
--with "pipefy-auth @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/auth" \
|
|
25
|
+
--from "git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/mcp" \
|
|
26
|
+
--refresh pipefy-mcp-server
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The `--with pipefy` / `pipefy-auth` flags are required pre-1.0 because the workspace members are not yet on PyPI. At v1.0 this collapses to `uvx pipefy-mcp-server`.
|
|
30
|
+
|
|
31
|
+
For per-client wiring (Claude Code / Cursor / Claude Desktop / Codex), see [root `README.md#installation`](../../README.md#installation).
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Set the following environment variables (or add them to a `.env` file in the working directory, or pin them in `~/.config/pipefy/config.toml`):
|
|
36
|
+
|
|
37
|
+
```env
|
|
38
|
+
PIPEFY_SERVICE_ACCOUNT_CLIENT_ID=your_client_id
|
|
39
|
+
PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET=your_client_secret
|
|
40
|
+
# Non-prod environments only:
|
|
41
|
+
# PIPEFY_BASE_URL=https://<your-api-host>
|
|
42
|
+
# PIPEFY_AUTH_URL=https://<your-signin-host>/realms/<realm>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`PIPEFY_BASE_URL` defaults to `https://app.pipefy.com` (drives the four API endpoints) and `PIPEFY_AUTH_URL` defaults to `https://signin.pipefy.com/realms/pipefy` (the OIDC issuer). Set them only for non-prod environments.
|
|
46
|
+
|
|
47
|
+
Full reference (every `PIPEFY_*` variable, validation rules, TOML schema, precedence chain): [`docs/config.md`](../../docs/config.md).
|
|
48
|
+
|
|
49
|
+
## Edge cases and alternative wiring
|
|
50
|
+
|
|
51
|
+
### macOS keychain `errSecParam (-25244)`
|
|
52
|
+
|
|
53
|
+
`pipefy auth login` may exit with `errSecParam (-25244)` at the final keychain-write step even though OAuth itself succeeded. The cause is not yet reliably diagnosed — direct `keyring.set_password` calls from the same uv-tool-installed Python succeed under repro testing, so this is likely a transient `Security.framework` condition rather than a deterministic per-binary ACL problem. If it occurs, retry the slash command (Claude Code) or `pipefy auth login` (terminal) first; as a fallback, run `pipefy auth login` once from a regular Terminal.app session and approve any macOS keychain dialog that appears. [Issue #235](https://github.com/pipefy/ai-toolkit/issues/235) tracks platform-aware error messaging.
|
|
54
|
+
|
|
55
|
+
### Claude Code: `claude mcp add` (per-project terminal flow)
|
|
56
|
+
|
|
57
|
+
Useful when you want to wire the server without editing `~/.claude.json` by hand:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
claude mcp add --scope project pipefy \
|
|
61
|
+
-- uvx \
|
|
62
|
+
--with "pipefy @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/sdk" \
|
|
63
|
+
--with "pipefy-auth @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/auth" \
|
|
64
|
+
--from "git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/mcp" \
|
|
65
|
+
pipefy-mcp-server
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then (repeat for each key you need):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
claude mcp add-env pipefy PIPEFY_SERVICE_ACCOUNT_CLIENT_ID <YOUR_CLIENT_ID>
|
|
72
|
+
claude mcp add-env pipefy PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET <YOUR_CLIENT_SECRET>
|
|
73
|
+
# Non-prod environments only:
|
|
74
|
+
# claude mcp add-env pipefy PIPEFY_BASE_URL https://<your-api-host>
|
|
75
|
+
# claude mcp add-env pipefy PIPEFY_AUTH_URL https://<your-signin-host>/realms/<realm>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Claude Code: settings edit (post-plugin install)
|
|
79
|
+
|
|
80
|
+
The plugin's `.mcp.json` ships `command` + `args` only. To set the `env` block, edit the `pipefy` server entry in `~/.claude.json` (or the Claude Code settings UI):
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"pipefy": {
|
|
86
|
+
"env": {
|
|
87
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_ID": "<CLIENT_ID>",
|
|
88
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET": "<CLIENT_SECRET>"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A live MCP server picks up rotated credentials on its next tool call; if the server failed to start because credentials were missing, restart it (or restart Claude Code) after login completes.
|
|
96
|
+
|
|
97
|
+
### Local-clone alternative (contributors)
|
|
98
|
+
|
|
99
|
+
If you have a clone of this repo and want the MCP server to use it directly (without `uvx` fetching from git), launch via `uv run` from the clone:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"pipefy": {
|
|
105
|
+
"command": "uv",
|
|
106
|
+
"args": [
|
|
107
|
+
"run",
|
|
108
|
+
"--directory",
|
|
109
|
+
"/absolute/path/to/pipefy-mcp-server",
|
|
110
|
+
"pipefy-mcp-server"
|
|
111
|
+
],
|
|
112
|
+
"env": {
|
|
113
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_ID": "<CLIENT_ID>",
|
|
114
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET": "<CLIENT_SECRET>"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This form also works as a per-project `.mcp.json` if your team shares a clone. Committing `.mcp.json` without secrets (placeholders or env injection) keeps team setups consistent.
|
|
122
|
+
|
|
123
|
+
### Legacy environment variables
|
|
124
|
+
|
|
125
|
+
`PIPEFY_OAUTH_CLIENT` and `PIPEFY_OAUTH_SECRET` still resolve to the new `PIPEFY_SERVICE_ACCOUNT_*` names with a one-shot stderr deprecation warning. The aliases will be removed in a later `0.2.0-beta.x` release. The `PIPEFY_OAUTH_URL` alias was dropped — set `PIPEFY_BASE_URL` instead. Migration notes: [`docs/MIGRATION.md#service-account-env-var-rename`](../../docs/MIGRATION.md#service-account-env-var-rename).
|
|
126
|
+
|
|
127
|
+
## Tools
|
|
128
|
+
|
|
129
|
+
**152 tools** across ten domains (including **Portals**) — see the root [`README.md`](../../README.md#mcp-server) for the full table with per-area links. Deep reference: [`docs/mcp/tools/`](../../docs/mcp/tools/cross-cutting.md) (start with [`cross-cutting.md`](../../docs/mcp/tools/cross-cutting.md)); portals: [`portal.md`](../../docs/mcp/tools/portal.md).
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
From the **repository root**:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
uv sync
|
|
137
|
+
uv run pytest packages/mcp/tests # MCP tests in isolation
|
|
138
|
+
uv run ruff check packages/mcp/src # lint
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
See the root [`README.md`](../../README.md) and [`AGENTS.md`](../../AGENTS.md) for contributor guidance.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# pipefy-mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for Pipefy — **152 tools** for AI agents (Cursor, Claude Desktop, Claude Code, Codex, and any MCP-compatible client). Depends on [`pipefy`](../sdk/README.md) for all GraphQL and API logic.
|
|
4
|
+
|
|
5
|
+
## Install (pre-launch, v0.1 → v0.5)
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uvx \
|
|
9
|
+
--with "pipefy @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/sdk" \
|
|
10
|
+
--with "pipefy-auth @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/auth" \
|
|
11
|
+
--from "git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/mcp" \
|
|
12
|
+
--refresh pipefy-mcp-server
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The `--with pipefy` / `pipefy-auth` flags are required pre-1.0 because the workspace members are not yet on PyPI. At v1.0 this collapses to `uvx pipefy-mcp-server`.
|
|
16
|
+
|
|
17
|
+
For per-client wiring (Claude Code / Cursor / Claude Desktop / Codex), see [root `README.md#installation`](../../README.md#installation).
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
Set the following environment variables (or add them to a `.env` file in the working directory, or pin them in `~/.config/pipefy/config.toml`):
|
|
22
|
+
|
|
23
|
+
```env
|
|
24
|
+
PIPEFY_SERVICE_ACCOUNT_CLIENT_ID=your_client_id
|
|
25
|
+
PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET=your_client_secret
|
|
26
|
+
# Non-prod environments only:
|
|
27
|
+
# PIPEFY_BASE_URL=https://<your-api-host>
|
|
28
|
+
# PIPEFY_AUTH_URL=https://<your-signin-host>/realms/<realm>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`PIPEFY_BASE_URL` defaults to `https://app.pipefy.com` (drives the four API endpoints) and `PIPEFY_AUTH_URL` defaults to `https://signin.pipefy.com/realms/pipefy` (the OIDC issuer). Set them only for non-prod environments.
|
|
32
|
+
|
|
33
|
+
Full reference (every `PIPEFY_*` variable, validation rules, TOML schema, precedence chain): [`docs/config.md`](../../docs/config.md).
|
|
34
|
+
|
|
35
|
+
## Edge cases and alternative wiring
|
|
36
|
+
|
|
37
|
+
### macOS keychain `errSecParam (-25244)`
|
|
38
|
+
|
|
39
|
+
`pipefy auth login` may exit with `errSecParam (-25244)` at the final keychain-write step even though OAuth itself succeeded. The cause is not yet reliably diagnosed — direct `keyring.set_password` calls from the same uv-tool-installed Python succeed under repro testing, so this is likely a transient `Security.framework` condition rather than a deterministic per-binary ACL problem. If it occurs, retry the slash command (Claude Code) or `pipefy auth login` (terminal) first; as a fallback, run `pipefy auth login` once from a regular Terminal.app session and approve any macOS keychain dialog that appears. [Issue #235](https://github.com/pipefy/ai-toolkit/issues/235) tracks platform-aware error messaging.
|
|
40
|
+
|
|
41
|
+
### Claude Code: `claude mcp add` (per-project terminal flow)
|
|
42
|
+
|
|
43
|
+
Useful when you want to wire the server without editing `~/.claude.json` by hand:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
claude mcp add --scope project pipefy \
|
|
47
|
+
-- uvx \
|
|
48
|
+
--with "pipefy @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/sdk" \
|
|
49
|
+
--with "pipefy-auth @ git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/auth" \
|
|
50
|
+
--from "git+https://github.com/pipefy/ai-toolkit@latest#subdirectory=packages/mcp" \
|
|
51
|
+
pipefy-mcp-server
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then (repeat for each key you need):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add-env pipefy PIPEFY_SERVICE_ACCOUNT_CLIENT_ID <YOUR_CLIENT_ID>
|
|
58
|
+
claude mcp add-env pipefy PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET <YOUR_CLIENT_SECRET>
|
|
59
|
+
# Non-prod environments only:
|
|
60
|
+
# claude mcp add-env pipefy PIPEFY_BASE_URL https://<your-api-host>
|
|
61
|
+
# claude mcp add-env pipefy PIPEFY_AUTH_URL https://<your-signin-host>/realms/<realm>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Claude Code: settings edit (post-plugin install)
|
|
65
|
+
|
|
66
|
+
The plugin's `.mcp.json` ships `command` + `args` only. To set the `env` block, edit the `pipefy` server entry in `~/.claude.json` (or the Claude Code settings UI):
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"pipefy": {
|
|
72
|
+
"env": {
|
|
73
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_ID": "<CLIENT_ID>",
|
|
74
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET": "<CLIENT_SECRET>"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A live MCP server picks up rotated credentials on its next tool call; if the server failed to start because credentials were missing, restart it (or restart Claude Code) after login completes.
|
|
82
|
+
|
|
83
|
+
### Local-clone alternative (contributors)
|
|
84
|
+
|
|
85
|
+
If you have a clone of this repo and want the MCP server to use it directly (without `uvx` fetching from git), launch via `uv run` from the clone:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"pipefy": {
|
|
91
|
+
"command": "uv",
|
|
92
|
+
"args": [
|
|
93
|
+
"run",
|
|
94
|
+
"--directory",
|
|
95
|
+
"/absolute/path/to/pipefy-mcp-server",
|
|
96
|
+
"pipefy-mcp-server"
|
|
97
|
+
],
|
|
98
|
+
"env": {
|
|
99
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_ID": "<CLIENT_ID>",
|
|
100
|
+
"PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET": "<CLIENT_SECRET>"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This form also works as a per-project `.mcp.json` if your team shares a clone. Committing `.mcp.json` without secrets (placeholders or env injection) keeps team setups consistent.
|
|
108
|
+
|
|
109
|
+
### Legacy environment variables
|
|
110
|
+
|
|
111
|
+
`PIPEFY_OAUTH_CLIENT` and `PIPEFY_OAUTH_SECRET` still resolve to the new `PIPEFY_SERVICE_ACCOUNT_*` names with a one-shot stderr deprecation warning. The aliases will be removed in a later `0.2.0-beta.x` release. The `PIPEFY_OAUTH_URL` alias was dropped — set `PIPEFY_BASE_URL` instead. Migration notes: [`docs/MIGRATION.md#service-account-env-var-rename`](../../docs/MIGRATION.md#service-account-env-var-rename).
|
|
112
|
+
|
|
113
|
+
## Tools
|
|
114
|
+
|
|
115
|
+
**152 tools** across ten domains (including **Portals**) — see the root [`README.md`](../../README.md#mcp-server) for the full table with per-area links. Deep reference: [`docs/mcp/tools/`](../../docs/mcp/tools/cross-cutting.md) (start with [`cross-cutting.md`](../../docs/mcp/tools/cross-cutting.md)); portals: [`portal.md`](../../docs/mcp/tools/portal.md).
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
From the **repository root**:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv sync
|
|
123
|
+
uv run pytest packages/mcp/tests # MCP tests in isolation
|
|
124
|
+
uv run ruff check packages/mcp/src # lint
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
See the root [`README.md`](../../README.md) and [`AGENTS.md`](../../AGENTS.md) for contributor guidance.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pipefy-mcp-server"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "MCP server that exposes Pipefy's GraphQL API."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"httpx>=0.27.0",
|
|
9
|
+
"mcp[cli]>=1.25.0",
|
|
10
|
+
# Workspace resolves versions in dev; PyPI releases stage these wheels alongside CLI/MCP (see release workflow).
|
|
11
|
+
"pipefy-auth",
|
|
12
|
+
"pipefy-infra",
|
|
13
|
+
"pipefy",
|
|
14
|
+
"pydantic>=2.13.4,<3",
|
|
15
|
+
"pydantic-settings>=2.8.1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.uv.sources]
|
|
19
|
+
pipefy-auth = { workspace = true }
|
|
20
|
+
pipefy-infra = { workspace = true }
|
|
21
|
+
pipefy = { workspace = true }
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
pipefy-mcp-server = "pipefy_mcp.main:main"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"aiohttp>=3.11.16",
|
|
29
|
+
"pytest>=8.3.5",
|
|
30
|
+
"pytest-asyncio>=0.26.0",
|
|
31
|
+
"pytest-cov>=6.1.1",
|
|
32
|
+
"pytest-mock>=3.14.0",
|
|
33
|
+
"respx>=0.22.0",
|
|
34
|
+
"ruff>=0.11.4",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["hatchling"]
|
|
39
|
+
build-backend = "hatchling.build"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/pipefy_mcp"]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.version]
|
|
45
|
+
path = "src/pipefy_mcp/__init__.py"
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
# `src` is the package; `../../packages/sdk/tests` supplies the `_shared` test bundle (live creds, fixtures) without putting the whole repo on sys.path.
|
|
50
|
+
pythonpath = ["src", "tests", "../../packages/sdk/tests"]
|
|
51
|
+
python_files = ["test_*.py"]
|
|
52
|
+
python_classes = ["Test*"]
|
|
53
|
+
python_functions = ["test_*"]
|
|
54
|
+
addopts = "-v --no-header --tb=short"
|
|
55
|
+
asyncio_mode = "auto"
|
|
56
|
+
asyncio_default_fixture_loop_scope = "module"
|
|
57
|
+
markers = [
|
|
58
|
+
"unit: marks a test as a unit test",
|
|
59
|
+
"integration: marks tests that call the live Pipefy GraphQL API (PIPEFY_* env)",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
extend = "../../pyproject.toml"
|
|
64
|
+
lint.extend-select = ["TID251"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
67
|
+
"pipefy_cli".msg = "pipefy_mcp must not import the CLI package; keep MCP independent of pipefy-cli."
|
|
68
|
+
"pipefy_sdk.services".msg = "Import from pipefy_sdk or pipefy_sdk.ai_* helpers only; pipefy_sdk.services is private to the SDK."
|
|
69
|
+
"pipefy_sdk.queries".msg = "Import from pipefy_sdk only; pipefy_sdk.queries is private to the SDK."
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Pipefy MCP Server package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
__version__ = "0.3.0-alpha.1"
|
|
8
|
+
version = __version__
|
|
9
|
+
|
|
10
|
+
m = re.match(r"^(\d+)\.(\d+)\.(\d+)", __version__)
|
|
11
|
+
if not m:
|
|
12
|
+
msg = f"__version__ must start with MAJOR.MINOR.PATCH, got {__version__!r}"
|
|
13
|
+
raise ValueError(msg)
|
|
14
|
+
version_tuple = (int(m[1]), int(m[2]), int(m[3]))
|
|
15
|
+
|
|
16
|
+
__all__ = ["__version__", "version", "version_tuple"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Resource-server auth for the HTTP transport: inbound bearer validation."""
|
|
2
|
+
|
|
3
|
+
from pipefy_mcp.auth.request_identity import (
|
|
4
|
+
RequestContextBearerAuth,
|
|
5
|
+
require_request_bearer,
|
|
6
|
+
)
|
|
7
|
+
from pipefy_mcp.auth.resource_server import (
|
|
8
|
+
JwtTokenVerifier,
|
|
9
|
+
ResourceServerAuth,
|
|
10
|
+
build_resource_server_auth,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"JwtTokenVerifier",
|
|
15
|
+
"RequestContextBearerAuth",
|
|
16
|
+
"ResourceServerAuth",
|
|
17
|
+
"build_resource_server_auth",
|
|
18
|
+
"require_request_bearer",
|
|
19
|
+
]
|