Skip to content

fix: stop advertising unsupported schedule mutations in SDK and MCP#48

Open
lorus-ipsum wants to merge 6 commits intoGenentech:mainfrom
lorus-ipsum:feat/23-remove-unsupported-schedule-mutations
Open

fix: stop advertising unsupported schedule mutations in SDK and MCP#48
lorus-ipsum wants to merge 6 commits intoGenentech:mainfrom
lorus-ipsum:feat/23-remove-unsupported-schedule-mutations

Conversation

@lorus-ipsum
Copy link
Copy Markdown

@lorus-ipsum lorus-ipsum commented Apr 2, 2026

Closes #23 — Stop advertising unsupported schedule mutations in SDK and MCP.

Why this issue

I reviewed all 19 open issues against three criteria: fix existing functionality over new features, no external dependency blockers, and achievable scope for a time-boxed session. Issue #23 stood out because it is a real API contract mismatch — the SDK, React hooks, and MCP tool registry all advertise schedule mutation operations that the service layer unconditionally rejects. This misleads both human developers and AI agents. It also touches Python and JavaScript across multiple architectural layers (service, transport, SDK, hooks), giving a good signal on cross-stack understanding without requiring Docker, API keys, or external services. Full analysis: assessment/issue_selection_recommendation.md.

Solution summary

The core fix removes replaceSchedule and deleteSchedule from the three public surfaces that advertised them: the Node SDK client, the useActivityGraph React hook, and the MCP tool registry. On top of that, I added 8 regression tests across Python and JavaScript, removed orphaned Pydantic schemas, marked the legacy REST endpoints as deprecated in OpenAPI, added a design note to the Fern SDK docs, and documented the service stubs. The work was structured as four incremental commits — each validated against a checklist before proceeding to the next. This is an intentional surface-area reduction, not a broken feature removal.

Planning artifacts: assessment/ contains the implementation plan, issue selection recommendation, and prompt history.


Acceptance Criteria Coverage

Completed:

  • Removed replaceSchedule(...) and deleteSchedule(...) from Move37Client in client.js
  • Removed replaceSchedule and deleteSchedule from the object returned by useActivityGraph(...)
  • Removed activity.schedule.replace and schedule.delete from McpToolRegistry definitions and handlers
  • Added tests covering the SDK, hook, MCP, and REST surface changes (8 new tests total)
  • REST routes intentionally left in place per issue scope

Additionally completed (beyond acceptance criteria):

  • Removed orphaned Pydantic schemas (ReplaceActivityScheduleInput, ScheduleEdgeInput) that became dead code after the MCP handler removal
  • Added MCP tools/call error-path tests proving removed tools return -32001 ("Unknown tool")
  • Marked both REST schedule endpoints as deprecated=True in FastAPI route decorators, flowing "deprecated": true and a 409 response description into the generated OpenAPI spec
  • Added "Schedule edge mutations" design note to Fern SDK docs (fern/pages/sdks.mdx)
  • Added docstrings to the replace_schedule and delete_schedule service stubs explaining they exist solely to back legacy REST routes

Not completed:

  • No SDK version bump (the SDK is pre-1.0; noted as deferred work below)
  • REST routes not removed (explicitly out of scope per issue and non-goals)

Validation

Commands run after each commit

# Python tests (API + services + scheduling + calendar + devtools)
python -m pytest src/move37/tests/ -v --tb=short

# SDK + React hook tests
cd src/move37/sdk/node && npx vitest run --reporter=verbose

# Web app build
cd src/move37/web && npm run build

# Contributor docs build
cd contributing-docs && npm run build

# OpenAPI export + deprecated flag verification (Stretch 2)
PYTHONPATH=src python fern/scripts/export_openapi.py
grep '"deprecated"' fern/openapi/move37.openapi.json

# Schema model_rebuild verification (Stretch 1)
PYTHONPATH=src python -c "from move37.api.schemas import NoteCreateResponse, NoteImportResponse; print('OK')"

# Repo-wide grep for removed symbols (CORE)
grep -r "replaceSchedule\|deleteSchedule" src/ --include="*.js" --include="*.py" | grep -v test
grep -r "activity.schedule.replace\|schedule.delete" src/move37/api/tool_registry.py

Results

Check Result
Python API tests 20/20 passed
Python full suite 36 passed, 3 pre-existing failures*
SDK tests 13 passed, 1 pre-existing failure**
Web build Clean (41 modules, 239KB JS)
Contributor docs build Clean
OpenAPI "deprecated": true Present on both schedule paths
Repo grep for removed symbols Zero hits outside test files
Schema model_rebuild() No import errors

* Pre-existing Python failures (verified identical on main by stashing changes and re-running):

  • test_fork_clears_scheduling_fieldsStopIteration (seed data issue)
  • test_disconnect_clears_owner_scoped_account — assertion mismatch
  • test_apply_updates_graph_dates_and_syncs_calendar — date-dependent (uses today's date vs hardcoded 2026-03-23)

** Pre-existing SDK failure (verified identical on main):

  • useAppleCalendarIntegration > loads status and supports connectingconnected field assertion

Prompt History

Full history with context: assessment/prompt-history.md

Key prompts that materially influenced the work:

Prompt 0: Read through the repository to better understand what this codebase does.
Prompt 1: Read the contributing-docs. What do I need to do first to set up before contributing?
Prompt 7: Review all open issues. Recommend a task using criteria: (1) most pressing given current state, prioritise fixing existing functionality over new features; (2) no dependency blockers; (3) appropriate for a few hours of work.
Prompt 9: Read the issue in detail and create a plan. Consider which files will need to be changed, how, and any anticipated pitfalls. Separate into core, additional and stretch goals.
Prompt 10: What would make this plan better/more robust?
Prompt 12: The validation checklist needs to be more thorough. I want checkboxes for each section that can be iterated through after completion.
Prompt 13: Begin executing the core requirements. Run all tests in the validation checklist. Make a note of failures and iterate until all pass. Do not commit without checking with me first.
Prompt 15: Review what has been done. Detail changes, their impact on functionality, and how I can verify them myself.
Prompt 17: Are there any more challenging stretch goals we can add to this plan?
Prompt 21: Create the PR. Move plans and prompt history to assessment/ and reference them. Read the PR instructions in detail to ensure all criteria are met.

AI Mistakes And Corrections

Issue: When first running the Python test suite, the assistant used unittest discover which failed with ImportError: Start directory is not importable because src/move37/tests/ lacked an __init__.py. The assistant created the file to fix the import path.

Correction: I identified this as a local environment issue (CI uses a different test runner configuration). The temporary __init__.py was removed after test execution and not committed. Subsequent runs used pytest which handles package discovery differently and does not require it.

Issue: The assistant initially wrote the MCP tools/list test by accessing self.client.app.state.mcp_transport directly and calling handle_request as a method call, bypassing the HTTP layer entirely.

Correction: I had the assistant rewrite the test to go through the actual HTTP endpoint (POST /v1/mcp/sse with a JSON-RPC payload), which exercises the full transport stack and matches how a real MCP client would interact with the API. This caught that the correct endpoint path is /v1/mcp/sse, not a direct object method call.

Issue: The assistant initially included unnecessary graph setup (GET /v1/graph) in the REST 409 tests, not realising that replace_schedule and delete_schedule raise ConflictError unconditionally without touching the database.

Correction: After reading the service layer stubs, I had the tests simplified to call the routes directly with arbitrary IDs, confirming the 409 is always returned regardless of graph state. This makes the tests faster and more clearly documents the stub behaviour.


Limitations And Deferred Work

  • SDK version bump: Removing replaceSchedule and deleteSchedule is a breaking change to the SDK's public API. The SDK is pre-1.0 (no package.json version field) so semver does not strictly require a major bump, but a changelog entry or version bump would be good practice in a follow-up.
  • MCP error code shift: Before this PR, an MCP client calling activity.schedule.replace received error code -32000 (ConflictError from the service layer). After this PR, it receives -32001 ("Unknown tool" from the transport layer). This is intentional — the tool should not be discoverable — but any MCP client that was catching -32000 specifically would need updating.
  • REST routes still exist: The issue explicitly scopes this to SDK/MCP cleanup. The REST routes (PUT /v1/activities/{id}/schedule, DELETE /v1/schedules/{a}/{b}) remain in place and always return 409. They are now marked deprecated=True in OpenAPI. A future PR could remove them entirely as a breaking REST change.
  • Orphaned REST schemas: ReplaceScheduleInput and SchedulePeerInput in schemas.py are only used by the deprecated REST routes. If those routes are removed in a future PR, these schemas should be removed too.
  • Pre-existing test failures: 3 Python tests and 1 SDK test fail on main (documented in Validation above). These are unrelated to this PR and should be addressed separately.

Candidate Checklist

  • I explained which issue acceptance criteria were completed, partially completed, or not completed.
  • I validated the change with tests and/or manual checks.
  • I included the key prompts and exploratory questions that materially influenced the work.
  • I documented any important assistant mistakes and how I corrected them.
  • If I changed direction, I explained why. (N/A — no change of direction)
  • I documented any limitations, deferred work, edge cases, or improvements I noticed but did not address.
  • I did not reveal my identity in this PR.
  • Delete assessment dir containing a full list of prompts and Claude plan mds before merging.

Remove unsupported schedule mutation advertisements from SDK and MCP surfaces.

Made-with: Cursor
The service layer always rejects replaceSchedule and deleteSchedule with
ConflictError because schedule edges are derived from startDate. Remove
these operations from the three public surfaces that still advertised
them:

- Move37Client: removed replaceSchedule() and deleteSchedule()
- useActivityGraph hook: removed replaceSchedule and deleteSchedule
- McpToolRegistry: removed activity.schedule.replace and schedule.delete
  tool definitions, handler entries, and handler methods

REST routes are intentionally left in place per issue Genentech#23 scope.

Closes Genentech#23

Made-with: Cursor
SDK client tests:
- replaceSchedule is not a property of Move37Client
- deleteSchedule is not a property of Move37Client

React hook test:
- useActivityGraph return object omits replaceSchedule/deleteSchedule

Python API tests:
- MCP tools/list response omits activity.schedule.replace and schedule.delete
- REST PUT /activities/{id}/schedule still returns 409 ConflictError
- REST DELETE /schedules/{a}/{b} still returns 409 ConflictError

Made-with: Cursor
Remove ReplaceActivityScheduleInput and ScheduleEdgeInput from
schemas.py. These were only referenced by the MCP handler methods
removed in the CORE commit and are now dead code. The REST-facing
schemas (ReplaceScheduleInput, SchedulePeerInput) remain in place.

Also add two tests proving that tools/call with the removed tool names
(activity.schedule.replace, schedule.delete) returns JSON-RPC error
code -32001 ("Unknown tool").

Made-with: Cursor
Mark both REST schedule mutation endpoints as deprecated in their
FastAPI route decorators so the generated OpenAPI spec shows
deprecated: true and a 409 response description. This ensures Fern
docs and any auto-generated SDK clients surface the unsupported status.

Add a "Schedule edge mutations" section to fern/pages/sdks.mdx
explaining that the Node SDK intentionally omits these methods and
directing users to updateActivity or /v1/scheduling/replan instead.

Add docstrings to the replace_schedule and delete_schedule service
stubs explaining they exist solely to back the legacy REST routes.

Made-with: Cursor
Move prompt history, implementation plan, and issue selection
recommendation to assessment/ for PR reference.

Made-with: Cursor
@lorus-ipsum
Copy link
Copy Markdown
Author

In hindsight, I think a better way to do this would be to write the PR with the original plan, including the core, additional and stretch chunks as well as the validation checklist for each. Then as each commit was made for each of the three chunks I could add comments detailing any deviations from the plan above and ticking off completed validation checklists. Finally add a comment such as the PR above, summarising the approach and steps taken.

I think with would result in a more auditable PR and demonstrate train of thought more clearly.

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.

Stop advertising unsupported schedule mutations in SDK and MCP

1 participant