Skip to content

Rebased foss-sandbox with upstream main#4

Closed
jawad-khan wants to merge 6 commits into
foss-sandboxfrom
jawad/rebase-foss-sandbox
Closed

Rebased foss-sandbox with upstream main#4
jawad-khan wants to merge 6 commits into
foss-sandboxfrom
jawad/rebase-foss-sandbox

Conversation

@jawad-khan

@jawad-khan jawad-khan commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Motivation / Background

Rebased foss-sandbox with upstream main.

  • Resolves #issue-id

Detail

Rebased foss-sandbox with upstream main.

Additional information

TIP: Provide additional information such as screenshots, benchmarks, reference to other repositories or alternative solutions

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one change. Changes that are unrelated should be opened in separate PRs
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature
  • CHANGELOG files are updated for the behavior change or additional feature (minor bug fixes and documentation changes should not be included)
  • This PR contains API changes and API documentation is updated accordingly (for critical or behavior change, please inform related parties about them).

jawad-khan and others added 6 commits June 4, 2026 18:50
Removed ~170 lines of over-engineered configurability that had safe,
fixed values in practice:

- AUTH_TYPE=SSO explicit opt-in replaces heuristic env-var sniffing
  (cognito_http_env_ready / cognito_http_configuration_intended)
- Dropped MCP_COGNITO_REDIRECT_URI — MCP_BASE_URL is always the public
  URL by definition (OAuth clients must reach it), so a separate
  redirect URI override is never needed
- Hardcoded COGNITO_OIDC_FORWARD_PKCE=false, COGNITO_TOKEN_ENDPOINT_AUTH_METHOD=none,
  COGNITO_REQUIRE_CONSENT=true, COGNITO_RELAX_OAUTH_RESOURCE_MISMATCH=true —
  all were already the defaults and never changed
- Inlined _plane_root_base() and _httpx_verify() one-use helpers
- Restored _plane_bearer_for info log (id_token forwarding); dropped the
  warning (we only use Cognito SSO, never Plane OAuth, so absence of
  id_token is never an actionable signal)
- Added targeted tests for _plane_bearer_for and id_token header preference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jawad-khan jawad-khan requested a review from Copilot June 5, 2026 11:41
@jawad-khan jawad-khan changed the title Jawad/rebase foss sandbox Rebased foss-sandbox with upstream main Jun 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly expands the Plane MCP server’s self-hosting and query capabilities by adding an AWS Cognito-backed HTTP mode, introducing Redis/AWS Secrets Manager-backed token storage selection, and evolving multiple MCP tools to support workspace slug bootstrap + PQL-based filtering.

Changes:

  • Add Cognito SSO HTTP transport (AUTH_TYPE=SSO) and supporting environment validation / callback handling.
  • Introduce configurable OAuth token storage (in-memory, Redis, Redis+password, ElastiCache+Secrets Manager) with eager connectivity checks.
  • Update and extend tool surface: workspace slug bootstrap (list_workspaces), PQL reference tooling, and multiple tools updated to accept workspace_slug overrides and/or return paginated envelopes.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 26 comments.

Show a summary per file
File Description
tests/test_workspaces_tool.py Adds unit tests for _plane_auth_headers behavior (Bearer vs X-Api-Key).
tests/test_cognito_http.py Adds tests for Cognito HTTP env validation and ID-token attachment behavior.
tests/test_client_context.py Adds tests for workspace slug resolution + ID-token bearer selection logic.
tests/test_aws_secrets.py Adds comprehensive tests for Secrets Manager caching + Redis token store selection paths.
README.md Documents Cognito HTTP self-hosted mode and adds list_workspaces to tool list.
pyproject.toml Updates dependencies (fastmcp/plane-sdk/etc) and modifies project version.
plane_mcp/tools/workspaces.py Adds list_workspaces, header construction helper, and workspace_slug overrides for workspace tools.
plane_mcp/tools/work_logs.py Adds optional workspace_slug override to work log tools.
plane_mcp/tools/work_items.py Major refactor: PQL-enabled list tools, paginated envelope returns, new helper tools, broader workspace_slug override work.
plane_mcp/tools/work_item_types.py Adds workspace-scoped type support and optional workspace_slug.
plane_mcp/tools/work_item_relations.py Adds optional workspace_slug and improves relation_type documentation formatting.
plane_mcp/tools/work_item_properties.py Expands property tooling (workspace/project/type scope), adds options/value APIs, and optional workspace_slug in some entry points.
plane_mcp/tools/work_item_links.py Adds optional workspace_slug override to link tools.
plane_mcp/tools/work_item_comments.py Adds optional workspace_slug override to comment tools.
plane_mcp/tools/work_item_activities.py Adds optional workspace_slug override to activity tools.
plane_mcp/tools/users.py Adds optional workspace_slug override and clarifies slug requirements in docstring.
plane_mcp/tools/states.py Adds optional workspace_slug override to state tools.
plane_mcp/tools/projects.py Adds optional workspace_slug overrides broadly; introduces project archive/unarchive and estimate management tools.
plane_mcp/tools/pql.py Adds on-demand get_pql_reference tool to reduce tool manifest size.
plane_mcp/tools/pql_reference.py Adds brief/full PQL reference strings and hint text for tool schemas.
plane_mcp/tools/pages.py Adds workspace/project page listing and work-item page link tools; partial workspace_slug override coverage.
plane_mcp/tools/modules.py Adds optional workspace_slug, PQL-enabled module work item listing, and renames issue_ids → work_item_ids at tool level.
plane_mcp/tools/milestones.py Adds optional workspace_slug overrides and renames issue_ids → work_item_ids at tool level.
plane_mcp/tools/labels.py Adds optional workspace_slug override to label tools.
plane_mcp/tools/intake.py Adds optional workspace_slug overrides and expands intake update semantics (status/snooze/duplicate).
plane_mcp/tools/initiatives.py Adds optional workspace_slug override to initiative tools.
plane_mcp/tools/epics.py Adds optional workspace_slug override to epic tools.
plane_mcp/tools/cycles.py Adds optional workspace_slug, PQL-enabled cycle work item listing, and new archive/complete behaviors.
plane_mcp/tools/__init__.py Registers the new PQL tool module.
plane_mcp/storage.py Adds token store selection + Redis reachability checks + optional Secrets Manager auth.
plane_mcp/server.py Switches OAuth transport token storage to build_token_store().
plane_mcp/cognito_http.py Adds Cognito SSO HTTP app, provider customization, env validation, and Starlette composition.
plane_mcp/client.py Adds _plane_bearer_for and enforces non-empty workspace slug resolution with clear error.
plane_mcp/aws_secrets.py Adds Secrets Manager helper with TTL cache + redis CredentialProvider integration.
plane_mcp/auth/plane_oauth_provider.py Tweaks Plane internal base URL resolution logic.
plane_mcp/__main__.py Adds AUTH_TYPE=SSO branch for Cognito HTTP mode and supports MCP_PATH_PREFIX mount prefixing.
.env.test Documents Redis/token storage and prefix env vars for local/self-hosted setups.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyproject.toml
Comment on lines 1 to 4
[project]
name = "plane-mcp-server"
version = "0.2.10"
version = "0.2.9"
description = "A Model Context Protocol server for Plane integration"
Comment thread plane_mcp/storage.py
Comment on lines +97 to +101
# REDIS_SSL defaults False here: plain Redis is the common case for
# static-password deployments. Set REDIS_SSL=true for TLS-fronted Redis.
use_ssl = _redis_ssl_enabled(default=False)
_ping_redis(redis_host, int(redis_port), password=password, ssl=use_ssl)
store = RedisStore(host=redis_host, port=int(redis_port), password=password)
Comment thread plane_mcp/cognito_http.py
Comment on lines +193 to +205
def _oauth_client_storage() -> MemoryStore | RedisStore:
global _oauth_kv_singleton
if _oauth_kv_singleton is not None:
return _oauth_kv_singleton
redis_host = os.getenv("REDIS_HOST", "").strip()
redis_port = os.getenv("REDIS_PORT", "").strip()
if redis_host and redis_port:
logger.info("Cognito HTTP: using Redis for OAuth storage")
_oauth_kv_singleton = RedisStore(host=redis_host, port=int(redis_port))
else:
logger.warning("Cognito HTTP: in-memory OAuth storage (set REDIS_HOST/REDIS_PORT for production)")
_oauth_kv_singleton = MemoryStore()
return _oauth_kv_singleton
Comment on lines +112 to +121
def list_workspace_work_items(
pql: Annotated[str | None, Field(description=PQL_FIELD_HINT)] = None,
order_by: str | None = None,
per_page: int | None = None,
cursor: str | None = None,
expand: str | None = None,
fields: str | None = None,
external_id: str | None = None,
external_source: str | None = None,
) -> dict[str, Any]:
next_cursor: Cursor for the next page.
prev_cursor: Cursor for the previous page.
"""
client, workspace_slug = get_plane_client_context()
Comment thread plane_mcp/tools/pages.py
Comment on lines +99 to +119
@mcp.tool()
def detach_page_from_work_item(
project_id: str,
work_item_id: str,
work_item_page_id: str,
) -> None:
"""
Remove a page link from a work item.

Args:
project_id: UUID of the project
work_item_id: UUID of the work item
work_item_page_id: UUID of the work item page link (not the page ID)
"""
client, workspace_slug = get_plane_client_context()
client.work_items.pages.delete(
workspace_slug=workspace_slug,
project_id=project_id,
work_item_id=work_item_id,
work_item_page_id=work_item_page_id,
)
return client.estimates.retrieve(workspace_slug=workspace_slug, project_id=project_id)

@mcp.tool()
def list_project_estimate_points(project_id: str, estimate_id: str) -> list[EstimatePoint]:
Returns:
List of EstimatePoint objects, each with id and value fields
"""
client, workspace_slug = get_plane_client_context()
Comment on lines +444 to +452
def create_project_estimate(
project_id: str,
name: str,
type: str | None = None,
description: str | None = None,
last_used: bool = True,
external_id: str | None = None,
external_source: str | None = None,
) -> Estimate:
Returns:
Created Estimate object
"""
client, workspace_slug = get_plane_client_context()
@hunzlahmalik

Copy link
Copy Markdown
Collaborator

Closing in favor of #5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants