Skip to content

fix(mcp): widen DependencyType to str so all bd dep types validate#3133

Merged
maphew merged 2 commits into
gastownhall:mainfrom
GGPrompts:fix/mcp-dependency-type-literal
Apr 10, 2026
Merged

fix(mcp): widen DependencyType to str so all bd dep types validate#3133
maphew merged 2 commits into
gastownhall:mainfrom
GGPrompts:fix/mcp-dependency-type-literal

Conversation

@GGPrompts

Copy link
Copy Markdown
Contributor

Problem

integrations/beads-mcp/src/beads_mcp/models.py:23 defines:

DependencyType = Literal["blocks", "related", "parent-child", "discovered-from"]

But the bd CLI emits ~19 dependency types (see internal/types/types.go:769-805):

blocks, parent-child, conditional-blocks, waits-for, related, discovered-from, replies-to, relates-to, duplicates, supersedes, authored-by, assigned-to, approved-by, attests, tracks, until, caused-by, validates, delegated-from

Any mcp__beads__show() call on an issue with a non-whitelisted dep edge crashes with pydantic literal_error. Repro from a real session today:

beads - show (MCP)(issue_id: "tn-b77d", brief: true, brief_deps: true)
  ⎿  Error: 1 validation error for Issue
     dependencies.0.dependency_type
       Input should be 'blocks', 'related', 'parent-child' or 'discovered-from'
     [type=literal_error, input_value='relates-to', input_type=str]

brief=true doesn't help — BriefDep reuses the same Literal. The CLI (bd show) is unaffected because it doesn't go through Pydantic.

This blocks any agent workflow that uses mcp__beads__show() over a knowledge graph that has relates-to, replies-to, duplicates, supersedes, etc. edges. PR #1469 added CLI display support for relates-to back in February — the MCP layer just never caught up.

Related: #1750 (MCP Pydantic validation error on list when issues have dependencies — same class of bug, different call site).

Fix

Change DependencyType from a Literal to a plain str, mirroring how IssueStatus and IssueType are already handled in the same file (lines 21-22). The bd CLI is the source of truth and validates dep types itself (DependencyType.IsValid() in internal/types/types.go:805); the MCP layer doesn't need a redundant whitelist that drifts from the CLI.

Also updated the user-facing docstrings in server.py and tools.py to stop claiming only 4 dep types exist — they now mention the common four and point to internal/types/types.go for the full list.

Verification

Synthesizing an Issue with a relates-to LinkedIssue now validates cleanly:

from beads_mcp.models import LinkedIssue, Issue
import datetime
now = datetime.datetime.now(datetime.timezone.utc)
linked = LinkedIssue(
    id='tn-test', title='test', status='open', priority=2, issue_type='task',
    created_at=now, updated_at=now, dependency_type='relates-to'
)
Issue(
    id='tn-parent', title='parent', status='open', priority=2, issue_type='task',
    created_at=now, updated_at=now, dependencies=[linked]
)
# OK — no validation error

Test suite: 141 unit tests pass, 7 model/dep-specific tests pass. Note: there are 4 pre-existing failures in tests/test_compaction_config.py that hardcode /Users/stevey/src/dave/beads/... paths — unrelated to this change, fail on any non-Steve machine.

Why a str and not an expanded Literal?

I considered listing all 19 types in the Literal, but:

  1. New dep types get added to internal/types/types.go over time (e.g., delegated-from is recent). A Literal will drift again.
  2. The MCP layer can't validate semantic correctness anyway — only the Go-side IsValid() method knows the live set.
  3. IssueStatus and IssueType already made the same call (and for the same reason — custom statuses/types via bd config set), so this is internally consistent.

If you'd prefer the literal approach for type safety / IDE autocomplete, happy to switch.

GGPrompts and others added 2 commits April 8, 2026 12:22
The Pydantic Literal at models.py:23 only whitelisted 4 dependency types
(blocks, related, parent-child, discovered-from), but the bd CLI emits ~19
(see internal/types/types.go: blocks, parent-child, conditional-blocks,
waits-for, related, discovered-from, replies-to, relates-to, duplicates,
supersedes, authored-by, assigned-to, approved-by, attests, tracks, until,
caused-by, validates, delegated-from).

Any mcp__beads__show() on an issue with a non-whitelisted dep edge crashed
with `pydantic literal_error`, e.g.:

    1 validation error for Issue
    dependencies.0.dependency_type
      Input should be 'blocks', 'related', 'parent-child' or 'discovered-from'
      [type=literal_error, input_value='relates-to', input_type=str]

Brief mode hit the same error because BriefDep reused the same Literal.

Fix: change DependencyType from a Literal to a plain `str`, mirroring how
IssueStatus and IssueType are already handled in the same file. The bd CLI
is the source of truth for valid dep types and validates them itself; the
MCP layer doesn't need a redundant whitelist that drifts from the CLI.

Also updated the user-facing docstrings in server.py and tools.py to stop
claiming only 4 dep types exist.

Verified: synthesizing an Issue with a 'relates-to' LinkedIssue now
validates cleanly. All 141 unit tests pass (the 4 failures in
test_compaction_config.py are pre-existing — they hardcode
/Users/stevey/src/dave/beads/... paths).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@maphew maphew merged commit e85475d into gastownhall:main Apr 10, 2026
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.

2 participants