Skip to content

fix: return success from module work item add tool#125

Open
OshriFatkiev wants to merge 1 commit into
makeplane:mainfrom
OshriFatkiev:fix/module-work-items-response
Open

fix: return success from module work item add tool#125
OshriFatkiev wants to merge 1 commit into
makeplane:mainfrom
OshriFatkiev:fix/module-work-items-response

Conversation

@OshriFatkiev
Copy link
Copy Markdown

@OshriFatkiev OshriFatkiev commented May 18, 2026

Description

Fixes add_work_items_to_module returning Unexpected response type even after the Plane API successfully adds the work item to the module.

The module work-item endpoint returns a JSON array of association records. Since the MCP tool was typed as returning None, that successful SDK response could still conflict with MCP response handling.

This PR changes add_work_items_to_module to return a structured success response after the SDK call completes, and adds a regression test covering the SDK returning a list.

It also fixes a few existing Ruff issues in tests so ruff check plane_mcp tests passes.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Not applicable.

Test Scenarios

uv run --no-sync ruff check plane_mcp tests
uv run --no-sync pytest tests/test_modules.py tests/test_stateless_http.py tests/test_oauth_security.py -q

Not run: live integration tests, because they require Plane credentials and a running MCP endpoint.

References

Fixes #124.

Summary by CodeRabbit

Release Notes

  • New Features

    • The add work items to module operation now returns a success confirmation response.
  • Bug Fixes

    • Improved JSON parsing error handling with more specific exception catching.
  • Tests

    • Added comprehensive test coverage for module work item operations.
  • Chores

    • Minor code organization improvements.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

The PR fixes add_work_items_to_module to return a typed success response instead of None, resolving MCP response validation errors that occurred despite successful API operations. The tool now declares and returns an AddWorkItemsToModuleResult TypedDict, includes comprehensive test coverage with fake client mocks, and includes minor supporting improvements to error handling and test file formatting.

Changes

Module Tool Success Response

Layer / File(s) Summary
Type contract and tool implementation
plane_mcp/tools/modules.py
AddWorkItemsToModuleResult TypedDict with success: bool field is defined. The add_work_items_to_module tool return type changes from None to this typed result, and the tool explicitly returns {"success": True} after the SDK call completes.
Test infrastructure and tool validation
tests/test_modules.py
Fake client classes record SDK arguments and responses. An async test monkeypatches the Plane client context, registers module tools on a FastMCP instance, invokes the tool through the MCP interface, and asserts both the MCP response and captured call arguments.
Supporting improvements
tests/test_integration.py, tests/test_oauth_security.py, tests/test_stateless_http.py
JSON error handling is narrowed to catch json.JSONDecodeError specifically. Test file imports and constants are adjusted for consistency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A module's success now speaks with truth,
No more "unexpected" in our youth—
Typed responses glow, tests validate,
The return path shines, no longer opaque! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: return success from module work item add tool' clearly and concisely summarizes the main change: addressing the tool's return type to properly report success.
Linked Issues check ✅ Passed The PR successfully addresses issue #124 by changing add_work_items_to_module to return a structured success response (AddWorkItemsToModuleResult TypedDict) instead of None, which resolves the 'Unexpected response type' error.
Out of Scope Changes check ✅ Passed All changes are in scope: the tool signature update, TypedDict definition, test file improvements (Ruff issues), and a new regression test directly address issue #124's requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
plane_mcp/tools/modules.py (1)

236-236: 💤 Low value

Consider capturing and logging the SDK response for observability.

The SDK's return value (likely a list of association records based on the PR context) is currently discarded. While returning {"success": True} aligns with the fix for issue #124, capturing the SDK response would improve debugging and observability.

♻️ Optional: Capture SDK response for logging
     client, workspace_slug = get_plane_client_context()
-    client.modules.add_work_items(
+    _response = client.modules.add_work_items(
         workspace_slug=workspace_slug,
         project_id=project_id,
         module_id=module_id,
         issue_ids=work_item_ids,
     )
+    # Could log _response here for debugging if needed
     return {"success": True}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plane_mcp/tools/modules.py` at line 236, The SDK call whose result is
currently discarded before the final return should be assigned to a variable
(e.g., sdk_response or associations_result), logged for observability (using the
module's logger at debug/info level), and included or summarized in the returned
payload instead of simply returning {"success": True}; update the code around
the SDK invocation and the final return (the place that currently just returns
{"success": True}) to capture the SDK response, call logger.debug/ info with
that variable, and return a JSON object that contains success plus a concise
representation of the SDK response.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plane_mcp/tools/modules.py`:
- Line 3: The local TypedDict AddWorkItemsToModuleResult must be removed and the
function add_work_items_to_module should return either None (preferred, matching
cycles.py and milestones.py) or a plane-sdk Pydantic model from plane.models;
update the function that calls client.modules.add_work_items() (which is a
side-effect operation) to return None and adjust its type hints and any callers
accordingly, or if you need structured output, replace the TypedDict with an
appropriate plane.models Pydantic class and return an instance of that model
instead of the custom TypedDict.

---

Nitpick comments:
In `@plane_mcp/tools/modules.py`:
- Line 236: The SDK call whose result is currently discarded before the final
return should be assigned to a variable (e.g., sdk_response or
associations_result), logged for observability (using the module's logger at
debug/info level), and included or summarized in the returned payload instead of
simply returning {"success": True}; update the code around the SDK invocation
and the final return (the place that currently just returns {"success": True})
to capture the SDK response, call logger.debug/ info with that variable, and
return a JSON object that contains success plus a concise representation of the
SDK response.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 74ce87d3-17da-4dfa-aa27-dc1ac9da5b5d

📥 Commits

Reviewing files that changed from the base of the PR and between da196de and 3649d48.

📒 Files selected for processing (5)
  • plane_mcp/tools/modules.py
  • tests/test_integration.py
  • tests/test_modules.py
  • tests/test_oauth_security.py
  • tests/test_stateless_http.py
💤 Files with no reviewable changes (2)
  • tests/test_oauth_security.py
  • tests/test_stateless_http.py

"""Module-related tools for Plane MCP Server."""

from typing import Any, get_args
from typing import Any, TypedDict, get_args
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check plane-sdk for the add_work_items return type and related Pydantic models

# Search for the add_work_items method definition
rg -n -A 10 'def add_work_items' --type py

# Search for Pydantic models related to module-issue associations
rg -n 'class.*Module.*Issue|class.*Issue.*Module' --type py

# Check imports and type hints in the modules client
ast-grep --pattern 'def add_work_items($$$) -> $RET_TYPE:
  $$$'

Repository: makeplane/plane-mcp-server

Length of output: 3515


🏁 Script executed:

# Read the modules.py file to see the TypedDict definition and implementation
cat -n plane_mcp/tools/modules.py | sed -n '1,40p'

Repository: makeplane/plane-mcp-server

Length of output: 1430


🏁 Script executed:

# Read the return statement area
cat -n plane_mcp/tools/modules.py | sed -n '213,240p'

Repository: makeplane/plane-mcp-server

Length of output: 1086


🏁 Script executed:

# Search for TypedDict definition in the file
rg -n 'class AddWorkItemsToModuleResult' -A 5 plane_mcp/tools/modules.py

Repository: makeplane/plane-mcp-server

Length of output: 230


🏁 Script executed:

# Check imports at the top of modules.py to see what's available from plane-sdk
cat -n plane_mcp/tools/modules.py | head -30

Repository: makeplane/plane-mcp-server

Length of output: 1036


🏁 Script executed:

# Search for plane-sdk Pydantic models related to modules or issues
find . -path ./node_modules -prune -o -type f -name "*.py" -exec grep -l "class.*Module.*Pydantic\|class.*BaseModel" {} \; | head -20

Repository: makeplane/plane-mcp-server

Length of output: 111


🏁 Script executed:

# Look for what client.modules.add_work_items actually returns by checking the call site
rg -n 'client\.modules\.add_work_items' -B 2 -A 5 plane_mcp/tools/modules.py

Repository: makeplane/plane-mcp-server

Length of output: 371


🏁 Script executed:

# Check if PaginatedModuleWorkItemResponse or similar could be used
rg -n 'PaginatedModuleWorkItemResponse' -B 2 -A 10 --type py | head -50

Repository: makeplane/plane-mcp-server

Length of output: 1643


🏁 Script executed:

# Search in plane-sdk imports to see what models are available for module operations
python3 << 'EOF'
import sys
# Check what pydantic models are imported from plane.models.modules
print("Models imported from plane.models.modules:")
models = ["CreateModule", "Module", "PaginatedArchivedModuleResponse", 
          "PaginatedModuleResponse", "PaginatedModuleWorkItemResponse", "UpdateModule"]
for m in models:
    print(f"  - {m}")
EOF

Repository: makeplane/plane-mcp-server

Length of output: 258


🏁 Script executed:

# Check if there's a ModuleWorkItem or similar model for the returned items
rg -n 'class.*Module.*Item|class.*ModuleIssue|ModuleWorkItem' --type py | grep -v test | head -20

Repository: makeplane/plane-mcp-server

Length of output: 252


🏁 Script executed:

# Verify what the actual plane-sdk client.modules.add_work_items method signature is
# by looking at any type hints or documentation in the codebase
rg -n 'modules.add_work_items' -B 3 -A 8 --type py

Repository: makeplane/plane-mcp-server

Length of output: 1439


🏁 Script executed:

# Check other tools in the codebase to see how they handle similar situations
rg -n 'def add_work_items_to' -A 15 plane_mcp/tools/*.py

Repository: makeplane/plane-mcp-server

Length of output: 3074


Replace custom TypedDict return type with a plane-sdk Pydantic model or None.

The coding guideline requires "Tool functions must return Pydantic models from plane-sdk", but AddWorkItemsToModuleResult is a locally-defined TypedDict (line 20). The underlying client.modules.add_work_items() call does not return data—it's a side-effect operation. Either return None (consistent with similar methods in cycles.py and milestones.py), or if structured output is needed, use an appropriate Pydantic model from the plane.models package rather than a custom TypedDict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plane_mcp/tools/modules.py` at line 3, The local TypedDict
AddWorkItemsToModuleResult must be removed and the function
add_work_items_to_module should return either None (preferred, matching
cycles.py and milestones.py) or a plane-sdk Pydantic model from plane.models;
update the function that calls client.modules.add_work_items() (which is a
side-effect operation) to return None and adjust its type hints and any callers
accordingly, or if you need structured output, replace the TypedDict with an
appropriate plane.models Pydantic class and return an instance of that model
instead of the custom TypedDict.

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.

add_work_items_to_module succeeds but returns "Unexpected response type"

1 participant