gabriel / musehub public
type-contracts.md markdown
1219 lines 42.8 KB
c4b6737d refactor: domain bleed purge — remove generation/LLM/DAW code from MuseHub Gabriel Cardona <gabriel@tellurstori.com> 6d ago
1 # Muse Hub — Type Contracts Reference
2
3 > Updated: 2026-03-17 | Covers every named entity in the Muse Hub surface:
4 > MIDI type aliases, JSON wire types, MCP protocol types, Pydantic API models,
5 > auth tokens, SSE event hierarchy, SQLAlchemy ORM models.
6 > `Any` and bare `list` / `dict` (without type arguments) do not appear in any
7 > production file. Every type boundary is named. The mypy strict ratchet
8 > enforces zero violations on every CI run across 111 source files.
9
10 ---
11
12 ## Table of Contents
13
14 1. [Design Philosophy](#design-philosophy)
15 2. [MIDI Type Aliases (`contracts/midi_types.py`)](#midi-type-aliases)
16 3. [JSON Type Aliases (`contracts/json_types.py`)](#json-type-aliases)
17 4. [JSON Wire TypedDicts (`contracts/json_types.py`)](#json-wire-typeddicts)
18 5. [MCP Protocol Types (`contracts/mcp_types.py`)](#mcp-protocol-types)
19 6. [Pydantic Base Types (`contracts/pydantic_types.py`, `models/base.py`)](#pydantic-base-types)
20 7. [Auth Types (`auth/tokens.py`)](#auth-types)
21 8. [Protocol Events (`protocol/events.py`)](#protocol-events)
22 9. [Protocol HTTP Responses (`protocol/responses.py`)](#protocol-http-responses)
23 10. [API Models (`models/musehub.py`)](#api-models)
24 11. [Database ORM Models (`db/`)](#database-orm-models)
25 12. [Entity Hierarchy](#entity-hierarchy)
26 13. [Entity Graphs (Mermaid)](#entity-graphs-mermaid)
27
28 ---
29
30 ## Design Philosophy
31
32 Every entity in this codebase follows five rules:
33
34 1. **No `Any`. No bare `object`. Ever.** Both collapse type safety for downstream
35 callers. Every boundary is typed with a concrete named entity — `TypedDict`,
36 `dataclass`, Pydantic model, or a specific union. The CI mypy strict ratchet
37 enforces zero violations.
38
39 2. **No covariance in collection aliases.** `dict[str, str]` and `list[str]`
40 are used directly. If a return mixes value types, a `TypedDict` names that
41 shape instead of a `dict[str, str | int]`.
42
43 3. **Boundaries own coercion.** When external data arrives (JSON over HTTP,
44 bytes from the database, MIDI off the wire), the boundary module coerces to
45 the canonical internal type. Downstream code always sees clean types.
46
47 4. **Wire-format TypedDicts for JSON serialisation, Pydantic models for HTTP.**
48 `TokenClaims`, `NoteDict`, `MCPRequest` etc. are JSON-serialisable and
49 used at IO boundaries. Pydantic `CamelModel` subclasses are used for all
50 FastAPI route return types and request bodies.
51
52 5. **No `# type: ignore`. Fix the underlying error instead.** The one designated
53 exception is the `json_list()` coercion boundary — a single
54 `type: ignore[arg-type]` inside the implementation body of that helper.
55
56 ### Banned → Use instead
57
58 | Banned | Use instead |
59 |--------|-------------|
60 | `Any` | `TypedDict`, `dataclass`, specific union |
61 | `object` | The actual type or a constrained union |
62 | `list` (bare) | `list[X]` with concrete element type |
63 | `dict` (bare) | `dict[K, V]` with concrete key/value types |
64 | `dict[str, X]` with known keys | `TypedDict` — name the keys |
65 | `Optional[X]` | `X \| None` |
66 | Legacy `List`, `Dict`, `Set`, `Tuple` | Lowercase builtins |
67 | `Union[A, B]` | `A \| B` |
68 | `Type[X]` | `type[X]` |
69 | `cast(T, x)` | Fix the callee to return `T` |
70 | `# type: ignore` | Fix the underlying type error |
71
72 ---
73
74 ## MIDI Type Aliases
75
76 **Path:** `musehub/contracts/midi_types.py`
77
78 Constrained `int` and `float` aliases via `Annotated[int, Field(...)]`.
79 Every MIDI boundary uses these instead of bare `int` so that range constraints
80 are part of the type signature and enforced by Pydantic at validation time.
81
82 | Alias | Base | Constraint | Description |
83 |-------|------|------------|-------------|
84 | `MidiPitch` | `int` | 0–127 | MIDI note number (C-1=0, Middle C=60, G9=127) |
85 | `MidiVelocity` | `int` | 0–127 | Note velocity; 0=note-off, 1–127=audible |
86 | `MidiChannel` | `int` | 0–15 | Zero-indexed MIDI channel; drums=9 |
87 | `MidiCC` | `int` | 0–127 | MIDI Control Change controller number |
88 | `MidiCCValue` | `int` | 0–127 | MIDI Control Change value |
89 | `MidiAftertouchValue` | `int` | 0–127 | Channel or poly aftertouch pressure |
90 | `MidiGMProgram` | `int` | 0–127 | General MIDI program / patch number (0-indexed) |
91 | `MidiPitchBend` | `int` | −8192–8191 | 14-bit signed pitch bend; 0=centre |
92 | `MidiBPM` | `int` | 20–300 | Tempo in beats per minute (always integer) |
93 | `BeatPosition` | `float` | ≥ 0.0 | Absolute beat position; fractional allowed |
94 | `BeatDuration` | `float` | > 0.0 | Duration in beats; must be strictly positive |
95 | `ArrangementBeat` | `int` | ≥ 0 | Bar-aligned beat offset for section-level timing |
96 | `ArrangementDuration` | `int` | ≥ 1 | Section duration in beats (bars × numerator) |
97 | `Bars` | `int` | ≥ 1 | Bar count; always a positive integer |
98
99 ---
100
101 ## JSON Type Aliases
102
103 **Path:** `musehub/contracts/json_types.py`
104
105 Type aliases for recursive and domain-specific JSON shapes.
106
107 | Alias | Definition | Description |
108 |-------|-----------|-------------|
109 | `JSONScalar` | `str \| int \| float \| bool \| None` | A JSON leaf value with no recursive structure |
110 | `JSONValue` | `str \| int \| float \| bool \| None \| list["JSONValue"] \| dict[str, "JSONValue"]` | Recursive JSON value — use sparingly, never in Pydantic models |
111 | `JSONObject` | `dict[str, JSONValue]` | A JSON object with an unknown key set |
112 | `InternalNoteDict` | `NoteDict` | Alias for `NoteDict` on the snake_case storage path |
113 | `RegionNotesMap` | `dict[str, list[NoteDict]]` | Maps `region_id` → ordered list of MIDI notes |
114 | `RegionCCMap` | `dict[str, list[CCEventDict]]` | Maps `region_id` → ordered list of MIDI CC events |
115 | `RegionPitchBendMap` | `dict[str, list[PitchBendDict]]` | Maps `region_id` → ordered list of MIDI pitch bend events |
116 | `RegionAftertouchMap` | `dict[str, list[AftertouchDict]]` | Maps `region_id` → ordered list of MIDI aftertouch events |
117 | `EventJsonSchema` | `dict[str, JSONValue]` | JSON Schema dict for a single event type, as produced by `model_json_schema()` |
118 | `EventSchemaMap` | `dict[str, EventJsonSchema]` | Maps `event_type` → its JSON Schema; returned by `/protocol/events.json` |
119
120 ---
121
122 ## JSON Wire TypedDicts
123
124 **Path:** `musehub/contracts/json_types.py`
125
126 These are the MIDI note and event shapes exchanged across service boundaries.
127 All use `total=False` where the producer may omit fields, and `Required[T]`
128 to mark fields that must always be present.
129
130 ### `NoteDict`
131
132 `TypedDict (total=False)` — A single MIDI note. Accepts both camelCase (wire
133 format) and snake_case (internal storage) to avoid a transform boundary.
134
135 | Field | Type | Description |
136 |-------|------|-------------|
137 | `pitch` | `MidiPitch` | MIDI pitch number |
138 | `velocity` | `MidiVelocity` | Note velocity |
139 | `channel` | `MidiChannel` | MIDI channel |
140 | `startBeat` | `BeatPosition` | Onset beat (camelCase wire) |
141 | `durationBeats` | `BeatDuration` | Duration in beats (camelCase wire) |
142 | `noteId` | `str` | Note UUID (camelCase wire) |
143 | `trackId` | `str` | Parent track UUID (camelCase wire) |
144 | `regionId` | `str` | Parent region UUID (camelCase wire) |
145 | `start_beat` | `BeatPosition` | Onset beat (snake_case storage) |
146 | `duration_beats` | `BeatDuration` | Duration in beats (snake_case storage) |
147 | `note_id` | `str` | Note UUID (snake_case storage) |
148 | `track_id` | `str` | Parent track UUID (snake_case storage) |
149 | `region_id` | `str` | Parent region UUID (snake_case storage) |
150 | `layer` | `str` | Optional layer grouping (e.g. `"melody"`) |
151
152 ### `CCEventDict`
153
154 `TypedDict` — A single MIDI Control Change event.
155
156 | Field | Type |
157 |-------|------|
158 | `cc` | `MidiCC` |
159 | `beat` | `BeatPosition` |
160 | `value` | `MidiCCValue` |
161
162 ### `PitchBendDict`
163
164 `TypedDict` — A single MIDI pitch bend event.
165
166 | Field | Type |
167 |-------|------|
168 | `beat` | `BeatPosition` |
169 | `value` | `MidiPitchBend` |
170
171 ### `AftertouchDict`
172
173 `TypedDict (total=False)` — MIDI aftertouch (channel or poly).
174
175 | Field | Type | Required |
176 |-------|------|----------|
177 | `beat` | `BeatPosition` | Yes |
178 | `value` | `MidiAftertouchValue` | Yes |
179 | `pitch` | `MidiPitch` | No (poly only) |
180
181 ### `SectionDict`
182
183 `TypedDict (total=False)` — A composition section (verse, chorus, bridge, etc.).
184 Used by the analysis service to describe structural section metadata.
185
186 | Field | Type |
187 |-------|------|
188 | `name` | `str` |
189 | `start_beat` | `float` |
190 | `length_beats` | `float` |
191 | `description` | `str` |
192 | `per_track_description` | `dict[str, str]` |
193
194 ### Conversion helpers
195
196 | Function | Signature | Description |
197 |----------|-----------|-------------|
198 | `is_note_dict(v)` | `(JSONValue) -> TypeGuard[NoteDict]` | Narrows `JSONValue` to `NoteDict` in list comprehensions |
199 | `jfloat(v, default)` | `(JSONValue, float) -> float` | Safe float extraction from `JSONValue` |
200 | `jint(v, default)` | `(JSONValue, int) -> int` | Safe int extraction from `JSONValue` |
201 | `json_list(items)` | overloaded | Coerces a `list[TypedDict]` to `list[JSONValue]` at insertion boundaries |
202
203 ---
204
205 ## MCP Protocol Types
206
207 **Path:** `musehub/contracts/mcp_types.py`
208
209 Typed shapes for the Model Context Protocol JSON-RPC 2.0 interface. TypedDicts
210 cover the wire format; Pydantic models (`MCPToolDefWire` etc.) are used for
211 FastAPI response serialisation.
212
213 ### Type Aliases
214
215 | Alias | Definition |
216 |-------|-----------|
217 | `MCPResponse` | `MCPSuccessResponse \| MCPErrorResponse` |
218 | `MCPMethodResponse` | `MCPInitializeResponse \| MCPToolsListResponse \| MCPCallResponse \| MCPSuccessResponse \| MCPErrorResponse` |
219
220 ### Request / Response TypedDicts
221
222 | Name | Kind | Description |
223 |------|------|-------------|
224 | `MCPRequest` | `total=False` | Incoming JSON-RPC 2.0 message from an MCP client |
225 | `MCPSuccessResponse` | required | JSON-RPC 2.0 success response |
226 | `MCPErrorDetail` | `total=False` | The `error` object inside a JSON-RPC error response |
227 | `MCPErrorResponse` | required | JSON-RPC 2.0 error response |
228 | `MCPInitializeParams` | `total=False` | Params for the `initialize` method |
229 | `MCPInitializeResult` | required | Result body for `initialize` |
230 | `MCPInitializeResponse` | required | Full response for `initialize` |
231 | `MCPToolsListResult` | required | Result body for `tools/list` |
232 | `MCPToolsListResponse` | required | Full response for `tools/list` |
233 | `MCPCallResult` | `total=False` | Result body for `tools/call` |
234 | `MCPCallResponse` | required | Full response for `tools/call` |
235
236 ### Tool Definition TypedDicts
237
238 | Name | Kind | Description |
239 |------|------|-------------|
240 | `MCPPropertyDef` | `total=False` | JSON Schema definition for a single tool property |
241 | `MCPInputSchema` | `total=False` | JSON Schema for a tool's accepted arguments |
242 | `MCPToolDef` | `total=False` | Complete definition of an MCP tool |
243 | `MCPContentBlock` | required | Content block in a tool result (`type`, `text`) |
244
245 ### Capability TypedDicts
246
247 | Name | Kind | Description |
248 |------|------|-------------|
249 | `MCPToolsCapability` | `total=False` | `tools` entry in `MCPCapabilities` |
250 | `MCPResourcesCapability` | `total=False` | `resources` entry in `MCPCapabilities` |
251 | `MCPCapabilities` | `total=False` | Server capabilities advertised during `initialize` |
252 | `MCPServerInfo` | required | Server info returned in `initialize` responses |
253 | `MCPCapabilitiesResult` | required | Capability block in `initialize` result |
254 | `MCPToolCallParams` | required | Params for `tools/call` |
255
256 ### DAW ↔ MCP Bridge TypedDicts
257
258 | Name | Kind | Description |
259 |------|------|-------------|
260 | `DAWToolCallMessage` | required | Message sent from MCP server to a DAW client over WebSocket |
261 | `DAWToolResponse` | `total=False` | Response sent from the DAW back after tool execution |
262
263 ### Pydantic Wire Models (FastAPI)
264
265 | Name | Description |
266 |------|-------------|
267 | `MCPPropertyDefWire` | Pydantic-safe JSON Schema property for FastAPI responses |
268 | `MCPInputSchemaWire` | Pydantic-safe tool input schema for FastAPI responses |
269 | `MCPToolDefWire` | Pydantic-safe tool definition for FastAPI route return types |
270
271 ---
272
273 ## Pydantic Base Types
274
275 **Path:** `musehub/contracts/pydantic_types.py`, `musehub/models/base.py`
276
277 ### `PydanticJson`
278
279 `RootModel[str | int | float | bool | None | list["PydanticJson"] | dict[str, "PydanticJson"]]`
280
281 The only safe recursive JSON field type for Pydantic models. Used wherever
282 a model field accepts an arbitrary JSON value (e.g. tool call parameters or
283 protocol introspection schemas). Replaces `dict[str, Any]` at every Pydantic
284 boundary.
285
286 **Helpers:**
287
288 | Function | Description |
289 |----------|-------------|
290 | `wrap(v: JSONValue) -> PydanticJson` | Convert a `JSONValue` to a `PydanticJson` instance |
291 | `unwrap(p: PydanticJson) -> JSONValue` | Convert a `PydanticJson` back to a `JSONValue` |
292 | `wrap_dict(d: dict[str, JSONValue]) -> dict[str, PydanticJson]` | Wrap each value in a dict |
293
294 ### `CamelModel`
295
296 `BaseModel` subclass. All Pydantic API models (request bodies and response
297 types) inherit from this. Configures:
298
299 - `alias_generator = to_camel` — fields are serialised to camelCase on the wire.
300 - `populate_by_name = True` — allows snake_case names in Python code.
301 - `extra = "ignore"` — unknown fields from clients are silently dropped.
302
303 ---
304
305 ## Auth Types
306
307 **Path:** `musehub/auth/tokens.py`
308
309 ### `TokenClaims`
310
311 `TypedDict (total=False)` — Decoded JWT payload returned by `validate_access_code`.
312 `type`, `iat`, and `exp` are always present (`Required`); `sub` and `role` are
313 optional claims added by the issuer.
314
315 | Field | Type | Required |
316 |-------|------|----------|
317 | `type` | `str` | Yes |
318 | `iat` | `int` | Yes |
319 | `exp` | `int` | Yes |
320 | `sub` | `str` | No |
321 | `role` | `str` | No |
322
323 ---
324
325 ## Protocol Events
326
327 **Path:** `musehub/protocol/events.py`
328
329 Protocol events are Pydantic `CamelModel` subclasses of `MuseEvent`, which
330 provides `type`, `seq`, and `protocol_version` on every payload. Muse Hub
331 defines two concrete event types — both in the MCP relay path.
332
333 ### Base Class
334
335 | Name | Kind | Description |
336 |------|------|-------------|
337 | `MuseEvent` | Pydantic (CamelModel) | Base class: `type: str`, `seq: int = -1`, `protocol_version: str` |
338
339 ### Concrete Event Types
340
341 | Event | `type` Literal | Description |
342 |-------|---------------|-------------|
343 | `MCPMessageEvent` | `"mcp.message"` | MCP tool-call message relayed over SSE; `payload: dict[str, object]` |
344 | `MCPPingEvent` | `"mcp.ping"` | MCP SSE keepalive heartbeat |
345
346 The event registry (`protocol/registry.py`) maps these two type strings to
347 their model classes and is frozen at import time.
348
349 ---
350
351 ## Protocol HTTP Responses
352
353 **Path:** `musehub/protocol/responses.py`
354
355 Pydantic response models for the four protocol introspection endpoints.
356 Fields use camelCase by declaration to match the wire format.
357
358 | Name | Route | Description |
359 |------|-------|-------------|
360 | `ProtocolInfoResponse` | `GET /protocol` | Version, hash, and registered event type list |
361 | `ProtocolEventsResponse` | `GET /protocol/events.json` | JSON Schema per event type |
362 | `ProtocolToolsResponse` | `GET /protocol/tools.json` | All registered MCP tool definitions |
363 | `ProtocolSchemaResponse` | `GET /protocol/schema.json` | Unified snapshot — version + hash + events + tools |
364
365 The protocol hash (`protocol/hash.py`) is a SHA-256 over the serialised event
366 schemas and tool schemas, computed deterministically at request time.
367
368 ---
369
370 ## API Models
371
372 **Path:** `musehub/models/musehub.py`
373
374 All are Pydantic `CamelModel` subclasses. Organized by domain feature.
375
376 ### Git / VCS
377
378 | Name | Description |
379 |------|-------------|
380 | `CommitInput` | A single commit in a push payload |
381 | `ObjectInput` | A binary object in a push payload |
382 | `PushRequest` | Body for `POST /repos/{id}/push` |
383 | `PushResponse` | Push confirmation with new branch head |
384 | `PullRequest` | Body for `POST /repos/{id}/pull` |
385 | `ObjectResponse` | Binary object returned in a pull response |
386 | `PullResponse` | Pull response — missing commits and objects |
387
388 ### Repositories
389
390 | Name | Description |
391 |------|-------------|
392 | `CreateRepoRequest` | Repository creation wizard body |
393 | `RepoResponse` | Wire representation of a Muse Hub repo |
394 | `TransferOwnershipRequest` | Transfer repo to another user |
395 | `RepoListResponse` | Paginated list of repos |
396 | `RepoStatsResponse` | Aggregated commit / branch / release counts |
397
398 ### Branches
399
400 | Name | Description |
401 |------|-------------|
402 | `BranchResponse` | Branch name and head commit pointer |
403 | `BranchListResponse` | Paginated list of branches |
404 | `BranchDivergenceScores` | Five-dimensional musical divergence scores |
405 | `BranchDetailResponse` | Branch with ahead/behind counts and divergence |
406 | `BranchDetailListResponse` | List of branches with detail |
407
408 ### Commits and Tags
409
410 | Name | Description |
411 |------|-------------|
412 | `CommitResponse` | Wire representation of a pushed commit |
413 | `CommitListResponse` | Paginated list of commits |
414 | `TagResponse` | A single tag entry |
415 | `TagListResponse` | All tags grouped by namespace |
416
417 ### Issues
418
419 | Name | Description |
420 |------|-------------|
421 | `IssueCreate` | Create issue body |
422 | `IssueUpdate` | Partial update body |
423 | `IssueResponse` | Wire representation of an issue |
424 | `IssueListResponse` | Paginated list of issues |
425 | `MusicalRef` | Parsed musical context reference (e.g. `track:bass`) |
426 | `IssueCommentCreate` | Create comment body |
427 | `IssueCommentResponse` | Wire representation of a comment |
428 | `IssueCommentListResponse` | Threaded discussion on an issue |
429 | `IssueAssignRequest` | Assign or unassign a user |
430 | `IssueLabelAssignRequest` | Replace the label list on an issue |
431
432 ### Milestones
433
434 | Name | Description |
435 |------|-------------|
436 | `MilestoneCreate` | Create milestone body |
437 | `MilestoneResponse` | Wire representation of a milestone |
438 | `MilestoneListResponse` | List of milestones |
439
440 ### Pull Requests
441
442 | Name | Description |
443 |------|-------------|
444 | `PRCreate` | Create PR body |
445 | `PRResponse` | Wire representation of a pull request |
446 | `PRListResponse` | Paginated list of pull requests |
447 | `PRMergeRequest` | Merge strategy selection body |
448 | `PRMergeResponse` | Merge confirmation |
449 | `PRDiffDimensionScore` | Per-dimension musical change score |
450 | `PRDiffResponse` | Musical diff between PR branches |
451 | `PRCommentCreate` | PR review comment body (supports four targeting granularities) |
452 | `PRCommentResponse` | Wire representation of a PR review comment |
453 | `PRCommentListResponse` | Threaded list of PR comments |
454 | `PRReviewerRequest` | Request reviewers |
455 | `PRReviewCreate` | Submit a formal review (approve / request_changes / comment) |
456 | `PRReviewResponse` | Wire representation of a review |
457 | `PRReviewListResponse` | List of reviews for a PR |
458
459 ### Releases
460
461 | Name | Description |
462 |------|-------------|
463 | `ReleaseCreate` | Create release body |
464 | `ReleaseDownloadUrls` | Structured download package URLs |
465 | `ReleaseResponse` | Wire representation of a release |
466 | `ReleaseListResponse` | List of releases |
467 | `ReleaseAssetCreate` | Attach asset to release |
468 | `ReleaseAssetResponse` | Wire representation of an asset |
469 | `ReleaseAssetListResponse` | Assets for a release |
470 | `ReleaseAssetDownloadCount` | Per-asset download count |
471 | `ReleaseDownloadStatsResponse` | Download counts for all assets |
472
473 ### Profile and Social
474
475 | Name | Description |
476 |------|-------------|
477 | `ProfileUpdateRequest` | Update profile body |
478 | `ProfileRepoSummary` | Compact repo summary on a profile page |
479 | `ProfileResponse` | Full wire representation of a user profile |
480 | `ContributorCredits` | Single contributor's credit record |
481 | `CreditsResponse` | Full credits roll for a repo |
482 | `StarResponse` | Star added / removed confirmation |
483 | `ContributionDay` | One day in the contribution heatmap |
484
485 ### Discovery and Search
486
487 | Name | Description |
488 |------|-------------|
489 | `ExploreRepoResult` | Public repo card on the explore page |
490 | `ExploreResponse` | Paginated discover response |
491 | `SearchCommitMatch` | Single commit returned by an in-repo search |
492 | `SearchResponse` | Response for all four in-repo search modes |
493 | `GlobalSearchCommitMatch` | Commit match in a cross-repo search |
494 | `GlobalSearchRepoGroup` | All matches for one repo in a cross-repo search |
495 | `GlobalSearchResult` | Top-level cross-repo search response |
496
497 ### Timeline, DAG, and Analytics
498
499 | Name | Description |
500 |------|-------------|
501 | `TimelineCommitEvent` | A commit plotted on the timeline |
502 | `TimelineEmotionEvent` | Emotion-vector data point on the timeline |
503 | `TimelineSectionEvent` | Detected section change marker |
504 | `TimelineTrackEvent` | Track addition or removal event |
505 | `TimelineResponse` | Chronological musical evolution timeline |
506 | `DivergenceDimensionResponse` | Per-dimension divergence score |
507 | `DivergenceResponse` | Full musical divergence report between two branches |
508 | `CommitDiffDimensionScore` | Per-dimension change score vs parent |
509 | `CommitDiffSummaryResponse` | Multi-dimensional diff summary |
510 | `DagNode` | Single commit node in the DAG |
511 | `DagEdge` | Directed edge in the commit DAG |
512 | `DagGraphResponse` | Topologically sorted commit graph |
513
514 ### Webhooks
515
516 | Name | Description |
517 |------|-------------|
518 | `WebhookCreate` | Create webhook body |
519 | `WebhookResponse` | Wire representation of a webhook |
520 | `WebhookListResponse` | List of webhooks |
521 | `WebhookDeliveryResponse` | Single delivery attempt |
522 | `WebhookDeliveryListResponse` | Delivery history |
523 | `WebhookRedeliverResponse` | Redeliver confirmation |
524 | `PushEventPayload` | TypedDict — push event webhook payload |
525 | `IssueEventPayload` | TypedDict — issue event webhook payload |
526 | `PullRequestEventPayload` | TypedDict (total=False) — PR event webhook payload |
527 | `WebhookEventPayload` | TypeAlias — `PushEventPayload \| IssueEventPayload \| PullRequestEventPayload` |
528
529 ### Context
530
531 | Name | Description |
532 |------|-------------|
533 | `MuseHubContextCommitInfo` | Minimal commit metadata in a context document |
534 | `MuseHubContextHistoryEntry` | Ancestor commit in evolutionary history |
535 | `MuseHubContextMusicalState` | Musical state at the target commit |
536 | `MuseHubContextResponse` | Full context document for a commit |
537
538 ### Objects
539
540 | Name | Description |
541 |------|-------------|
542 | `ObjectMetaResponse` | Artifact metadata (no content) |
543 | `ObjectMetaListResponse` | List of artifact metadata |
544
545 ---
546
547 ## Database ORM Models
548
549 **Path:** `musehub/db/`
550
551 All are SQLAlchemy ORM subclasses of a declarative `Base`.
552
553 ### `db/models.py` — Auth
554
555 | Model | Table | Description |
556 |-------|-------|-------------|
557 | `User` | `muse_users` | User account; `id` = JWT `sub` claim |
558 | `AccessToken` | `muse_access_tokens` | JWT token tracking — stores SHA-256 hash, never the raw token |
559
560 ### `db/muse_cli_models.py` — Muse CLI VCS
561
562 | Model | Table | Description |
563 |-------|-------|-------------|
564 | `MuseCliObject` | `muse_objects` | Content-addressed blob |
565 | `MuseCliSnapshot` | `muse_snapshots` | Immutable snapshot manifest |
566 | `MuseCliCommit` | `muse_commits` | Versioned commit pointing to a snapshot |
567 | `MuseCliTag` | `muse_tags` | Music-semantic tag on a CLI commit |
568
569 ### `db/musehub_models.py` — Muse Hub Core
570
571 | Model | Table | Description |
572 |-------|-------|-------------|
573 | `MusehubRepo` | `musehub_repos` | Remote repository with music-semantic metadata |
574 | `MusehubBranch` | `musehub_branches` | Named branch pointer |
575 | `MusehubCommit` | `musehub_commits` | Commit pushed to Muse Hub |
576 | `MusehubObject` | `musehub_objects` | Content-addressed binary artifact |
577 | `MusehubMilestone` | `musehub_milestones` | Milestone grouping issues |
578 | `MusehubIssueMilestone` | `musehub_issue_milestones` | Issue ↔ Milestone join table |
579 | `MusehubIssue` | `musehub_issues` | Issue opened against a repo |
580 | `MusehubIssueComment` | `musehub_issue_comments` | Threaded issue comment |
581 | `MusehubPullRequest` | `musehub_pull_requests` | Pull request |
582 | `MusehubPRReview` | `musehub_pr_reviews` | Formal PR review submission |
583 | `MusehubPRComment` | `musehub_pr_comments` | Inline musical diff comment on a PR |
584 | `MusehubRelease` | `musehub_releases` | Published version release |
585 | `MusehubReleaseAsset` | `musehub_release_assets` | Downloadable file attachment |
586 | `MusehubProfile` | `musehub_profiles` | Public user musical portfolio |
587 | `MusehubWebhook` | `musehub_webhooks` | Registered webhook subscription |
588 | `MusehubWebhookDelivery` | `musehub_webhook_deliveries` | Single webhook delivery attempt |
589 | `MusehubStar` | `musehub_stars` | User star on a public repo |
590 | `MusehubSession` | `musehub_sessions` | Recording session record pushed from the CLI |
591 | `MusehubComment` | `musehub_comments` | Threaded comment on any repo object |
592 | `MusehubReaction` | `musehub_reactions` | Emoji reaction on a comment or target |
593 | `MusehubFollow` | `musehub_follows` | User follows user — social graph |
594 | `MusehubWatch` | `musehub_watches` | User watches a repo for notifications |
595 | `MusehubNotification` | `musehub_notifications` | Notification delivered to a user |
596 | `MusehubFork` | `musehub_forks` | Fork relationship between two repos |
597 | `MusehubViewEvent` | `musehub_view_events` | Debounced repo view (one row per visitor per day) |
598 | `MusehubDownloadEvent` | `musehub_download_events` | Artifact export download event |
599 | `MusehubRenderJob` | `musehub_render_jobs` | Async MP3/piano-roll render job tracking |
600 | `MusehubEvent` | `musehub_events` | Append-only repo activity event stream |
601
602 ### `db/musehub_collaborator_models.py`
603
604 | Model | Table | Description |
605 |-------|-------|-------------|
606 | `MusehubCollaborator` | `musehub_collaborators` | Explicit push/admin access grant for a user on a repo |
607
608 ### `db/musehub_label_models.py`
609
610 | Model | Table | Description |
611 |-------|-------|-------------|
612 | `MusehubLabel` | `musehub_labels` | Coloured label tag scoped to a repo |
613 | `MusehubIssueLabel` | `musehub_issue_labels` | Issue ↔ Label join table |
614 | `MusehubPRLabel` | `musehub_pr_labels` | PR ↔ Label join table |
615
616 ### `db/musehub_stash_models.py`
617
618 | Model | Table | Description |
619 |-------|-------|-------------|
620 | `MusehubStash` | `musehub_stash` | Named save point for uncommitted changes |
621 | `MusehubStashEntry` | `musehub_stash_entries` | Single MIDI file snapshot within a stash |
622
623 ---
624
625 ## Entity Hierarchy
626
627 ```
628 Muse Hub
629
630 ├── MIDI Type Aliases (contracts/midi_types.py)
631 │ ├── MidiPitch, MidiVelocity, MidiChannel, MidiCC, MidiCCValue
632 │ ├── MidiAftertouchValue, MidiGMProgram, MidiPitchBend, MidiBPM
633 │ └── BeatPosition, BeatDuration, ArrangementBeat, ArrangementDuration, Bars
634
635 ├── JSON Types (contracts/json_types.py)
636 │ ├── Primitives: JSONScalar, JSONValue, JSONObject
637 │ ├── Region maps: RegionNotesMap, RegionCCMap, RegionPitchBendMap, RegionAftertouchMap
638 │ ├── Protocol aliases: EventJsonSchema, EventSchemaMap
639 │ └── TypedDicts: NoteDict, CCEventDict, PitchBendDict, AftertouchDict, SectionDict
640
641 ├── MCP Protocol (contracts/mcp_types.py)
642 │ ├── MCPRequest — TypedDict (total=False)
643 │ ├── MCPResponse — MCPSuccessResponse | MCPErrorResponse
644 │ ├── MCPMethodResponse — 5-way union of concrete response types
645 │ ├── MCPToolDef — TypedDict (total=False)
646 │ ├── DAWToolCallMessage — TypedDict
647 │ ├── DAWToolResponse — TypedDict (total=False)
648 │ └── MCPToolDefWire — Pydantic (FastAPI serialisation)
649
650 ├── Pydantic Base (contracts/pydantic_types.py, models/base.py)
651 │ ├── PydanticJson — RootModel: recursive JSON, safe for Pydantic fields
652 │ └── CamelModel — BaseModel: camelCase alias, populate_by_name, extra=ignore
653
654 ├── Auth (auth/tokens.py)
655 │ └── TokenClaims — TypedDict (total=False): decoded JWT payload
656
657 ├── Protocol Events (protocol/events.py)
658 │ ├── MuseEvent — Pydantic base: type + seq + protocol_version
659 │ ├── MCPMessageEvent — type: "mcp.message"; payload: dict[str, object]
660 │ └── MCPPingEvent — type: "mcp.ping" (keepalive)
661
662 ├── Protocol Introspection (protocol/)
663 │ ├── EVENT_REGISTRY — dict[str, type[MuseEvent]] (2 entries)
664 │ ├── compute_protocol_hash() — SHA-256 over events + tools schemas
665 │ ├── ProtocolInfoResponse — GET /protocol
666 │ ├── ProtocolEventsResponse — GET /protocol/events.json
667 │ ├── ProtocolToolsResponse — GET /protocol/tools.json
668 │ └── ProtocolSchemaResponse — GET /protocol/schema.json
669
670 ├── MCP Tools (mcp/)
671 │ ├── MUSEHUB_TOOLS — 7 read-only browsing tool definitions
672 │ ├── MUSEHUB_TOOL_NAMES — frozenset of tool name strings
673 │ ├── MCP_TOOLS — combined list of all registered tools
674 │ ├── TOOL_CATEGORIES — dict[str, str] tool → category
675 │ ├── MuseMCPServer — routes musehub_* calls to executor
676 │ └── ToolCallResult — server routing result (success, is_error, content)
677
678 ├── API Models (models/musehub.py) ~98 Pydantic models
679 │ ├── VCS: PushRequest, PullResponse, …
680 │ ├── Repos: CreateRepoRequest, RepoResponse, …
681 │ ├── Issues: IssueCreate, IssueResponse, IssueCommentResponse, …
682 │ ├── Pull Requests: PRCreate, PRResponse, PRDiffResponse, …
683 │ ├── Releases: ReleaseCreate, ReleaseResponse, ReleaseAssetResponse, …
684 │ ├── Profile + Social: ProfileResponse, StarResponse, …
685 │ ├── Discovery: ExploreResponse, SearchResponse, GlobalSearchResult, …
686 │ ├── Timeline + DAG: TimelineResponse, DagGraphResponse, …
687 │ └── Webhooks: WebhookCreate, WebhookDeliveryResponse, …
688
689 └── Database ORM (db/) 37 SQLAlchemy models
690 ├── Auth: User, AccessToken
691 ├── CLI VCS: MuseCliObject, MuseCliSnapshot, MuseCliCommit, MuseCliTag
692 ├── Hub Core: MusehubRepo, MusehubBranch, MusehubCommit, MusehubObject, …
693 ├── Social: MusehubStar, MusehubFollow, MusehubWatch, MusehubNotification, …
694 ├── Labels: MusehubLabel, MusehubIssueLabel, MusehubPRLabel
695 └── Stash: MusehubStash, MusehubStashEntry
696 ```
697
698 ---
699
700 ## Entity Graphs (Mermaid)
701
702 Arrow conventions:
703 - `*--` composition (owns, lifecycle-coupled)
704 - `-->` association (references)
705 - `..>` dependency (uses / produces)
706 - `..|>` implements / extends
707
708 ---
709
710 ### Diagram 1 — MIDI Note and Event Wire Types
711
712 The core typed shapes for MIDI data. `NoteDict` is dual-keyed (camelCase wire
713 + snake_case storage). The region maps alias these into domain containers.
714
715 ```mermaid
716 classDiagram
717 class NoteDict {
718 <<TypedDict total=False>>
719 pitch : MidiPitch
720 velocity : MidiVelocity
721 channel : MidiChannel
722 startBeat : BeatPosition
723 durationBeats : BeatDuration
724 noteId : str
725 trackId : str
726 regionId : str
727 start_beat : BeatPosition
728 duration_beats : BeatDuration
729 note_id : str
730 track_id : str
731 region_id : str
732 layer : str
733 }
734 class CCEventDict {
735 <<TypedDict>>
736 cc : MidiCC
737 beat : BeatPosition
738 value : MidiCCValue
739 }
740 class PitchBendDict {
741 <<TypedDict>>
742 beat : BeatPosition
743 value : MidiPitchBend
744 }
745 class AftertouchDict {
746 <<TypedDict total=False>>
747 beat : BeatPosition (Required)
748 value : MidiAftertouchValue (Required)
749 pitch : MidiPitch
750 }
751 class SectionDict {
752 <<TypedDict total=False>>
753 name : str
754 start_beat : float
755 length_beats : float
756 description : str
757 per_track_description : dict~str, str~
758 }
759 class RegionNotesMap {
760 <<TypeAlias>>
761 dict~str, list~NoteDict~~
762 }
763 class RegionCCMap {
764 <<TypeAlias>>
765 dict~str, list~CCEventDict~~
766 }
767 class RegionPitchBendMap {
768 <<TypeAlias>>
769 dict~str, list~PitchBendDict~~
770 }
771 class RegionAftertouchMap {
772 <<TypeAlias>>
773 dict~str, list~AftertouchDict~~
774 }
775
776 RegionNotesMap ..> NoteDict : list element
777 RegionCCMap ..> CCEventDict : list element
778 RegionPitchBendMap ..> PitchBendDict : list element
779 RegionAftertouchMap ..> AftertouchDict : list element
780 ```
781
782 ---
783
784 ### Diagram 2 — MCP Protocol Event Types
785
786 The two concrete events Muse Hub relays over SSE — both in the MCP path.
787 `MuseEvent` is the typed base; the event registry maps type strings to
788 model classes.
789
790 ```mermaid
791 classDiagram
792 class MuseEvent {
793 <<Pydantic CamelModel>>
794 +type : str
795 +seq : int = -1
796 +protocol_version : str
797 extra = "forbid"
798 }
799 class MCPMessageEvent {
800 <<Pydantic CamelModel>>
801 +type : Literal~"mcp.message"~
802 +payload : dict~str, object~
803 }
804 class MCPPingEvent {
805 <<Pydantic CamelModel>>
806 +type : Literal~"mcp.ping"~
807 }
808 class EVENT_REGISTRY {
809 <<dict frozen at import>>
810 "mcp.message" : MCPMessageEvent
811 "mcp.ping" : MCPPingEvent
812 }
813
814 MCPMessageEvent --|> MuseEvent
815 MCPPingEvent --|> MuseEvent
816 EVENT_REGISTRY ..> MCPMessageEvent : registers
817 EVENT_REGISTRY ..> MCPPingEvent : registers
818 ```
819
820 ---
821
822 ### Diagram 3 — Protocol Introspection
823
824 The hash computation pipeline and the four HTTP response types it drives.
825
826 ```mermaid
827 classDiagram
828 class ProtocolInfoResponse {
829 <<Pydantic BaseModel>>
830 +protocolVersion : str
831 +protocolHash : str
832 +eventTypes : list~str~
833 +eventCount : int
834 }
835 class ProtocolEventsResponse {
836 <<Pydantic BaseModel>>
837 +protocolVersion : str
838 +events : dict~str, PydanticJson~
839 }
840 class ProtocolToolsResponse {
841 <<Pydantic BaseModel>>
842 +protocolVersion : str
843 +tools : list~MCPToolDefWire~
844 +toolCount : int
845 }
846 class ProtocolSchemaResponse {
847 <<Pydantic BaseModel>>
848 +protocolVersion : str
849 +protocolHash : str
850 +events : dict~str, PydanticJson~
851 +tools : list~MCPToolDefWire~
852 +toolCount : int
853 +eventCount : int
854 }
855 class MCPToolDefWire {
856 <<Pydantic BaseModel>>
857 +name : str
858 +description : str
859 +inputSchema : MCPInputSchemaWire
860 }
861
862 ProtocolSchemaResponse ..> MCPToolDefWire : tools list
863 ProtocolToolsResponse ..> MCPToolDefWire : tools list
864 ProtocolSchemaResponse ..> ProtocolEventsResponse : events subset
865 ProtocolSchemaResponse ..> ProtocolToolsResponse : tools subset
866 ```
867
868 ---
869
870 ### Diagram 4 — MCP Tool Routing
871
872 How a tool name flows from an incoming request through the server routing layer
873 to the executor and back as a `ToolCallResult`.
874
875 ```mermaid
876 classDiagram
877 class MuseMCPServer {
878 +call_tool(name: str, params: dict) ToolCallResult
879 -_execute_musehub_tool(name, params) MusehubToolResult
880 -_build_result(result: MusehubToolResult) ToolCallResult
881 }
882 class ToolCallResult {
883 <<dataclass>>
884 +success : bool
885 +is_error : bool
886 +bad_request : bool
887 +content : list~dict~str, str~~
888 }
889 class MusehubToolResult {
890 <<dataclass frozen>>
891 +ok : bool
892 +data : dict~str, JSONValue~
893 +error_code : MusehubErrorCode | None
894 +error_message : str | None
895 }
896 class MusehubErrorCode {
897 <<Literal>>
898 "not_found"
899 "invalid_dimension"
900 "invalid_mode"
901 "db_unavailable"
902 }
903 class MUSEHUB_TOOLS {
904 <<list of MCPToolDef>>
905 musehub_browse_repo
906 musehub_list_branches
907 musehub_list_commits
908 musehub_read_file
909 musehub_get_analysis
910 musehub_search
911 musehub_get_context
912 }
913
914 MuseMCPServer --> ToolCallResult : returns
915 MuseMCPServer ..> MusehubToolResult : executor produces
916 MusehubToolResult --> MusehubErrorCode : error_code
917 MuseMCPServer ..> MUSEHUB_TOOLS : routes via MUSEHUB_TOOL_NAMES
918 ```
919
920 ---
921
922 ### Diagram 5 — MCP JSON-RPC Wire Types
923
924 The full JSON-RPC 2.0 type hierarchy used by the MCP HTTP adapter.
925
926 ```mermaid
927 classDiagram
928 class MCPRequest {
929 <<TypedDict total=False>>
930 +jsonrpc : str
931 +id : str | int | None
932 +method : str
933 +params : dict~str, object~
934 }
935 class MCPSuccessResponse {
936 <<TypedDict>>
937 +jsonrpc : str
938 +id : str | int | None
939 +result : object
940 }
941 class MCPErrorDetail {
942 <<TypedDict total=False>>
943 +code : int
944 +message : str
945 +data : object
946 }
947 class MCPErrorResponse {
948 <<TypedDict>>
949 +jsonrpc : str
950 +id : str | int | None
951 +error : MCPErrorDetail
952 }
953 class MCPToolDef {
954 <<TypedDict total=False>>
955 +name : str
956 +description : str
957 +inputSchema : MCPInputSchema
958 +server_side : bool
959 }
960 class MCPContentBlock {
961 <<TypedDict>>
962 +type : str
963 +text : str
964 }
965 class MCPCallResult {
966 <<TypedDict total=False>>
967 +content : list~MCPContentBlock~
968 +isError : bool
969 }
970
971 MCPErrorResponse *-- MCPErrorDetail : error
972 MCPCallResult *-- MCPContentBlock : content
973 MCPToolDef ..> MCPContentBlock : tools/call returns
974 ```
975
976 ---
977
978 ### Diagram 6 — Auth and JWT
979
980 Token lifecycle from issuance through validation to the decoded `TokenClaims`.
981
982 ```mermaid
983 classDiagram
984 class TokenClaims {
985 <<TypedDict total=False>>
986 +type : str (Required)
987 +iat : int (Required)
988 +exp : int (Required)
989 +sub : str
990 +role : str
991 }
992 class AccessToken {
993 <<SQLAlchemy ORM>>
994 +id : int
995 +token_hash : str
996 +user_id : str
997 +created_at : datetime
998 +expires_at : datetime
999 +revoked : bool
1000 }
1001 class User {
1002 <<SQLAlchemy ORM>>
1003 +id : str (JWT sub)
1004 +username : str
1005 +email : str
1006 +created_at : datetime
1007 }
1008
1009 AccessToken --> User : user_id FK
1010 TokenClaims ..> User : sub maps to User.id
1011 TokenClaims ..> AccessToken : validated against token_hash
1012 ```
1013
1014 ---
1015
1016 ### Diagram 7 — Muse Hub Repository Object Graph
1017
1018 The core VCS entity relationships in the database.
1019
1020 ```mermaid
1021 classDiagram
1022 class MusehubRepo {
1023 <<SQLAlchemy ORM>>
1024 +id : str
1025 +name : str
1026 +owner_id : str
1027 +description : str
1028 +is_public : bool
1029 +created_at : datetime
1030 }
1031 class MusehubBranch {
1032 <<SQLAlchemy ORM>>
1033 +id : str
1034 +repo_id : str (FK)
1035 +name : str
1036 +head_commit_id : str
1037 }
1038 class MusehubCommit {
1039 <<SQLAlchemy ORM>>
1040 +id : str
1041 +repo_id : str (FK)
1042 +branch_id : str (FK)
1043 +snapshot_id : str
1044 +message : str
1045 +committed_at : datetime
1046 }
1047 class MusehubObject {
1048 <<SQLAlchemy ORM>>
1049 +id : str
1050 +repo_id : str (FK)
1051 +commit_id : str (FK)
1052 +path : str
1053 +content_hash : str
1054 +mime_type : str
1055 }
1056 class MusehubIssue {
1057 <<SQLAlchemy ORM>>
1058 +id : str
1059 +repo_id : str (FK)
1060 +title : str
1061 +state : str
1062 +created_at : datetime
1063 }
1064 class MusehubPullRequest {
1065 <<SQLAlchemy ORM>>
1066 +id : str
1067 +repo_id : str (FK)
1068 +source_branch : str
1069 +target_branch : str
1070 +state : str
1071 }
1072 class MusehubRelease {
1073 <<SQLAlchemy ORM>>
1074 +id : str
1075 +repo_id : str (FK)
1076 +tag : str
1077 +title : str
1078 +created_at : datetime
1079 }
1080
1081 MusehubRepo *-- MusehubBranch : branches
1082 MusehubRepo *-- MusehubCommit : commits
1083 MusehubRepo *-- MusehubObject : objects
1084 MusehubRepo *-- MusehubIssue : issues
1085 MusehubRepo *-- MusehubPullRequest : pull requests
1086 MusehubRepo *-- MusehubRelease : releases
1087 MusehubBranch --> MusehubCommit : head_commit_id
1088 MusehubCommit --> MusehubObject : objects
1089 ```
1090
1091 ---
1092
1093 ### Diagram 8 — Social and Discovery Graph
1094
1095 User-to-user and user-to-repo relationships powering the social feed and
1096 discovery pages.
1097
1098 ```mermaid
1099 classDiagram
1100 class User {
1101 <<SQLAlchemy ORM>>
1102 +id : str
1103 +username : str
1104 }
1105 class MusehubProfile {
1106 <<SQLAlchemy ORM>>
1107 +user_id : str (FK)
1108 +display_name : str
1109 +bio : str
1110 }
1111 class MusehubStar {
1112 <<SQLAlchemy ORM>>
1113 +user_id : str (FK)
1114 +repo_id : str (FK)
1115 +starred_at : datetime
1116 }
1117 class MusehubFollow {
1118 <<SQLAlchemy ORM>>
1119 +follower_id : str (FK)
1120 +followee_id : str (FK)
1121 +created_at : datetime
1122 }
1123 class MusehubWatch {
1124 <<SQLAlchemy ORM>>
1125 +user_id : str (FK)
1126 +repo_id : str (FK)
1127 }
1128 class MusehubNotification {
1129 <<SQLAlchemy ORM>>
1130 +id : str
1131 +user_id : str (FK)
1132 +event_type : str
1133 +read : bool
1134 }
1135 class MusehubRepo {
1136 <<SQLAlchemy ORM>>
1137 +id : str
1138 +name : str
1139 }
1140
1141 User *-- MusehubProfile : profile
1142 User ..> MusehubStar : stars
1143 User ..> MusehubFollow : follows / followed_by
1144 User ..> MusehubWatch : watches
1145 User ..> MusehubNotification : receives
1146 MusehubStar --> MusehubRepo : repo_id
1147 MusehubWatch --> MusehubRepo : repo_id
1148 ```
1149
1150 ---
1151
1152 ### Diagram 9 — Full Entity Overview
1153
1154 All named layers and the dependency flow between them.
1155
1156 ```mermaid
1157 classDiagram
1158 class MIDIAliases {
1159 <<contracts/midi_types.py>>
1160 MidiPitch · MidiVelocity · BeatPosition · BeatDuration · …
1161 }
1162 class JSONTypes {
1163 <<contracts/json_types.py>>
1164 NoteDict · CCEventDict · PitchBendDict · AftertouchDict · SectionDict
1165 JSONValue · JSONObject · RegionNotesMap · EventJsonSchema
1166 }
1167 class MCPTypes {
1168 <<contracts/mcp_types.py>>
1169 MCPRequest · MCPResponse · MCPToolDef · DAWToolCallMessage
1170 MCPToolDefWire (Pydantic)
1171 }
1172 class PydanticBase {
1173 <<contracts/pydantic_types.py + models/base.py>>
1174 PydanticJson (RootModel)
1175 CamelModel (BaseModel)
1176 }
1177 class AuthTypes {
1178 <<auth/tokens.py>>
1179 TokenClaims (TypedDict)
1180 }
1181 class ProtocolEvents {
1182 <<protocol/events.py>>
1183 MuseEvent (base)
1184 MCPMessageEvent · MCPPingEvent
1185 }
1186 class ProtocolResponses {
1187 <<protocol/responses.py>>
1188 ProtocolInfoResponse
1189 ProtocolEventsResponse
1190 ProtocolToolsResponse
1191 ProtocolSchemaResponse
1192 }
1193 class MCPTools {
1194 <<mcp/>>
1195 MUSEHUB_TOOLS (7 tools)
1196 MuseMCPServer · ToolCallResult
1197 MusehubToolResult
1198 }
1199 class APIModels {
1200 <<models/musehub.py>>
1201 ~98 Pydantic CamelModel subclasses
1202 VCS · Repos · Issues · PRs · Releases · Social · Search
1203 }
1204 class DatabaseORM {
1205 <<db/>>
1206 37 SQLAlchemy models
1207 User · AccessToken · MusehubRepo · MusehubBranch · MusehubCommit · …
1208 }
1209
1210 JSONTypes ..> MIDIAliases : constrained int/float aliases
1211 MCPTypes ..> PydanticBase : MCPToolDefWire extends CamelModel
1212 ProtocolEvents ..> PydanticBase : MuseEvent extends CamelModel
1213 ProtocolResponses ..> PydanticBase : all extend BaseModel
1214 ProtocolResponses ..> MCPTypes : MCPToolDefWire in tools fields
1215 MCPTools ..> MCPTypes : MUSEHUB_TOOLS match MCPToolDef shape
1216 MCPTools ..> JSONTypes : MusehubToolResult.data uses JSONValue
1217 APIModels ..> PydanticBase : all extend CamelModel
1218 AuthTypes ..> DatabaseORM : TokenClaims.sub → User.id
1219 ```