gabriel / musehub public
type-contracts.md markdown
1616 lines 63.3 KB
c0f0b481 release: merge dev → main (#5) Gabriel Cardona <cgcardona@gmail.com> 5d ago
1 # MuseHub — Type Contracts Reference
2
3 > Updated: 2026-03-17 | MCP Protocol: **2025-11-25** | Covers every named entity in the MuseHub surface:
4 > MIDI type aliases, JSON wire types, MCP protocol types (including Elicitation, Session, and SSE),
5 > Pydantic API models, auth tokens, SSE event hierarchy, SQLAlchemy ORM models, and the full
6 > MCP integration layer (Streamable HTTP transport, session management, dispatcher, resources,
7 > prompts, read tools, write tools, elicitation-powered tools).
8 > `Any` and bare `list` / `dict` (without type arguments) do not appear in any
9 > production file. Every type boundary is named. The mypy strict ratchet
10 > enforces zero violations on every CI run across 111 source files.
11
12 ---
13
14 ## Table of Contents
15
16 1. [Design Philosophy](#design-philosophy)
17 2. [MIDI Type Aliases (`contracts/midi_types.py`)](#midi-type-aliases)
18 3. [JSON Type Aliases (`contracts/json_types.py`)](#json-type-aliases)
19 4. [JSON Wire TypedDicts (`contracts/json_types.py`)](#json-wire-typeddicts)
20 5. [MCP Protocol Types (`contracts/mcp_types.py`)](#mcp-protocol-types)
21 6. [MCP Integration Layer (`mcp/`)](#mcp-integration-layer)
22 7. [Pydantic Base Types (`contracts/pydantic_types.py`, `models/base.py`)](#pydantic-base-types)
23 8. [Auth Types (`auth/tokens.py`)](#auth-types)
24 9. [Protocol Events (`protocol/events.py`)](#protocol-events)
25 10. [Protocol HTTP Responses (`protocol/responses.py`)](#protocol-http-responses)
26 11. [API Models (`models/musehub.py`)](#api-models)
27 12. [Database ORM Models (`db/`)](#database-orm-models)
28 13. [Entity Hierarchy](#entity-hierarchy)
29 14. [Entity Graphs (Mermaid)](#entity-graphs-mermaid)
30
31 ---
32
33 ## Design Philosophy
34
35 Every entity in this codebase follows five rules:
36
37 1. **No `Any`. No bare `object`. Ever.** Both collapse type safety for downstream
38 callers. Every boundary is typed with a concrete named entity — `TypedDict`,
39 `dataclass`, Pydantic model, or a specific union. The CI mypy strict ratchet
40 enforces zero violations.
41
42 2. **No covariance in collection aliases.** `dict[str, str]` and `list[str]`
43 are used directly. If a return mixes value types, a `TypedDict` names that
44 shape instead of a `dict[str, str | int]`.
45
46 3. **Boundaries own coercion.** When external data arrives (JSON over HTTP,
47 bytes from the database, MIDI off the wire), the boundary module coerces to
48 the canonical internal type. Downstream code always sees clean types.
49
50 4. **Wire-format TypedDicts for JSON serialisation, Pydantic models for HTTP.**
51 `TokenClaims`, `NoteDict`, `MCPRequest` etc. are JSON-serialisable and
52 used at IO boundaries. Pydantic `CamelModel` subclasses are used for all
53 FastAPI route return types and request bodies.
54
55 5. **No `# type: ignore`. Fix the underlying error instead.** The one designated
56 exception is the `json_list()` coercion boundary — a single
57 `type: ignore[arg-type]` inside the implementation body of that helper.
58
59 ### Banned → Use instead
60
61 | Banned | Use instead |
62 |--------|-------------|
63 | `Any` | `TypedDict`, `dataclass`, specific union |
64 | `object` | The actual type or a constrained union |
65 | `list` (bare) | `list[X]` with concrete element type |
66 | `dict` (bare) | `dict[K, V]` with concrete key/value types |
67 | `dict[str, X]` with known keys | `TypedDict` — name the keys |
68 | `Optional[X]` | `X \| None` |
69 | Legacy `List`, `Dict`, `Set`, `Tuple` | Lowercase builtins |
70 | `Union[A, B]` | `A \| B` |
71 | `Type[X]` | `type[X]` |
72 | `cast(T, x)` | Fix the callee to return `T` |
73 | `# type: ignore` | Fix the underlying type error |
74
75 ---
76
77 ## MIDI Type Aliases
78
79 **Path:** `musehub/contracts/midi_types.py`
80
81 Constrained `int` and `float` aliases via `Annotated[int, Field(...)]`.
82 Every MIDI boundary uses these instead of bare `int` so that range constraints
83 are part of the type signature and enforced by Pydantic at validation time.
84
85 | Alias | Base | Constraint | Description |
86 |-------|------|------------|-------------|
87 | `MidiPitch` | `int` | 0–127 | MIDI note number (C-1=0, Middle C=60, G9=127) |
88 | `MidiVelocity` | `int` | 0–127 | Note velocity; 0=note-off, 1–127=audible |
89 | `MidiChannel` | `int` | 0–15 | Zero-indexed MIDI channel; drums=9 |
90 | `MidiCC` | `int` | 0–127 | MIDI Control Change controller number |
91 | `MidiCCValue` | `int` | 0–127 | MIDI Control Change value |
92 | `MidiAftertouchValue` | `int` | 0–127 | Channel or poly aftertouch pressure |
93 | `MidiGMProgram` | `int` | 0–127 | General MIDI program / patch number (0-indexed) |
94 | `MidiPitchBend` | `int` | −8192–8191 | 14-bit signed pitch bend; 0=centre |
95 | `MidiBPM` | `int` | 20–300 | Tempo in beats per minute (always integer) |
96 | `BeatPosition` | `float` | ≥ 0.0 | Absolute beat position; fractional allowed |
97 | `BeatDuration` | `float` | > 0.0 | Duration in beats; must be strictly positive |
98 | `ArrangementBeat` | `int` | ≥ 0 | Bar-aligned beat offset for section-level timing |
99 | `ArrangementDuration` | `int` | ≥ 1 | Section duration in beats (bars × numerator) |
100 | `Bars` | `int` | ≥ 1 | Bar count; always a positive integer |
101
102 ---
103
104 ## JSON Type Aliases
105
106 **Path:** `musehub/contracts/json_types.py`
107
108 Type aliases for recursive and domain-specific JSON shapes.
109
110 | Alias | Definition | Description |
111 |-------|-----------|-------------|
112 | `JSONScalar` | `str \| int \| float \| bool \| None` | A JSON leaf value with no recursive structure |
113 | `JSONValue` | `str \| int \| float \| bool \| None \| list["JSONValue"] \| dict[str, "JSONValue"]` | Recursive JSON value — use sparingly, never in Pydantic models |
114 | `JSONObject` | `dict[str, JSONValue]` | A JSON object with an unknown key set |
115 | `InternalNoteDict` | `NoteDict` | Alias for `NoteDict` on the snake_case storage path |
116 | `RegionNotesMap` | `dict[str, list[NoteDict]]` | Maps `region_id` → ordered list of MIDI notes |
117 | `RegionCCMap` | `dict[str, list[CCEventDict]]` | Maps `region_id` → ordered list of MIDI CC events |
118 | `RegionPitchBendMap` | `dict[str, list[PitchBendDict]]` | Maps `region_id` → ordered list of MIDI pitch bend events |
119 | `RegionAftertouchMap` | `dict[str, list[AftertouchDict]]` | Maps `region_id` → ordered list of MIDI aftertouch events |
120 | `EventJsonSchema` | `dict[str, JSONValue]` | JSON Schema dict for a single event type, as produced by `model_json_schema()` |
121 | `EventSchemaMap` | `dict[str, EventJsonSchema]` | Maps `event_type` → its JSON Schema; returned by `/protocol/events.json` |
122
123 ---
124
125 ## JSON Wire TypedDicts
126
127 **Path:** `musehub/contracts/json_types.py`
128
129 These are the MIDI note and event shapes exchanged across service boundaries.
130 All use `total=False` where the producer may omit fields, and `Required[T]`
131 to mark fields that must always be present.
132
133 ### `NoteDict`
134
135 `TypedDict (total=False)` — A single MIDI note. Accepts both camelCase (wire
136 format) and snake_case (internal storage) to avoid a transform boundary.
137
138 | Field | Type | Description |
139 |-------|------|-------------|
140 | `pitch` | `MidiPitch` | MIDI pitch number |
141 | `velocity` | `MidiVelocity` | Note velocity |
142 | `channel` | `MidiChannel` | MIDI channel |
143 | `startBeat` | `BeatPosition` | Onset beat (camelCase wire) |
144 | `durationBeats` | `BeatDuration` | Duration in beats (camelCase wire) |
145 | `noteId` | `str` | Note UUID (camelCase wire) |
146 | `trackId` | `str` | Parent track UUID (camelCase wire) |
147 | `regionId` | `str` | Parent region UUID (camelCase wire) |
148 | `start_beat` | `BeatPosition` | Onset beat (snake_case storage) |
149 | `duration_beats` | `BeatDuration` | Duration in beats (snake_case storage) |
150 | `note_id` | `str` | Note UUID (snake_case storage) |
151 | `track_id` | `str` | Parent track UUID (snake_case storage) |
152 | `region_id` | `str` | Parent region UUID (snake_case storage) |
153 | `layer` | `str` | Optional layer grouping (e.g. `"melody"`) |
154
155 ### `CCEventDict`
156
157 `TypedDict` — A single MIDI Control Change event.
158
159 | Field | Type |
160 |-------|------|
161 | `cc` | `MidiCC` |
162 | `beat` | `BeatPosition` |
163 | `value` | `MidiCCValue` |
164
165 ### `PitchBendDict`
166
167 `TypedDict` — A single MIDI pitch bend event.
168
169 | Field | Type |
170 |-------|------|
171 | `beat` | `BeatPosition` |
172 | `value` | `MidiPitchBend` |
173
174 ### `AftertouchDict`
175
176 `TypedDict (total=False)` — MIDI aftertouch (channel or poly).
177
178 | Field | Type | Required |
179 |-------|------|----------|
180 | `beat` | `BeatPosition` | Yes |
181 | `value` | `MidiAftertouchValue` | Yes |
182 | `pitch` | `MidiPitch` | No (poly only) |
183
184 ### `SectionDict`
185
186 `TypedDict (total=False)` — A composition section (verse, chorus, bridge, etc.).
187 Used by the analysis service to describe structural section metadata.
188
189 | Field | Type |
190 |-------|------|
191 | `name` | `str` |
192 | `start_beat` | `float` |
193 | `length_beats` | `float` |
194 | `description` | `str` |
195 | `per_track_description` | `dict[str, str]` |
196
197 ### Conversion helpers
198
199 | Function | Signature | Description |
200 |----------|-----------|-------------|
201 | `is_note_dict(v)` | `(JSONValue) -> TypeGuard[NoteDict]` | Narrows `JSONValue` to `NoteDict` in list comprehensions |
202 | `jfloat(v, default)` | `(JSONValue, float) -> float` | Safe float extraction from `JSONValue` |
203 | `jint(v, default)` | `(JSONValue, int) -> int` | Safe int extraction from `JSONValue` |
204 | `json_list(items)` | overloaded | Coerces a `list[TypedDict]` to `list[JSONValue]` at insertion boundaries |
205
206 ---
207
208 ## MCP Protocol Types
209
210 **Path:** `musehub/contracts/mcp_types.py`
211
212 Typed shapes for the Model Context Protocol 2025-11-25 JSON-RPC 2.0 interface.
213 TypedDicts cover the wire format; Pydantic models (`MCPToolDefWire` etc.) are
214 used for FastAPI response serialisation.
215
216 ### Type Aliases
217
218 | Alias | Definition |
219 |-------|-----------|
220 | `MCPResponse` | `MCPSuccessResponse \| MCPErrorResponse` |
221 | `MCPMethodResponse` | `MCPInitializeResponse \| MCPToolsListResponse \| MCPCallResponse \| MCPSuccessResponse \| MCPErrorResponse` |
222
223 ### Request / Response TypedDicts
224
225 | Name | Kind | Description |
226 |------|------|-------------|
227 | `MCPRequest` | `total=False` | Incoming JSON-RPC 2.0 message from an MCP client |
228 | `MCPSuccessResponse` | required | JSON-RPC 2.0 success response |
229 | `MCPErrorDetail` | `total=False` | The `error` object inside a JSON-RPC error response |
230 | `MCPErrorResponse` | required | JSON-RPC 2.0 error response |
231 | `MCPInitializeParams` | `total=False` | Params for the `initialize` method |
232 | `MCPInitializeResult` | required | Result body for `initialize` |
233 | `MCPInitializeResponse` | required | Full response for `initialize` |
234 | `MCPToolsListResult` | required | Result body for `tools/list` |
235 | `MCPToolsListResponse` | required | Full response for `tools/list` |
236 | `MCPCallResult` | `total=False` | Result body for `tools/call` |
237 | `MCPCallResponse` | required | Full response for `tools/call` |
238
239 ### Tool Definition TypedDicts
240
241 | Name | Kind | Description |
242 |------|------|-------------|
243 | `MCPPropertyDef` | `total=False` | JSON Schema definition for a single tool property |
244 | `MCPInputSchema` | `total=False` | JSON Schema for a tool's accepted arguments |
245 | `MCPToolDef` | `total=False` | Complete definition of an MCP tool |
246 | `MCPContentBlock` | required | Content block in a tool result (`type`, `text`) |
247
248 ### Capability TypedDicts
249
250 | Name | Kind | Description |
251 |------|------|-------------|
252 | `MCPToolsCapability` | `total=False` | `tools` entry in `MCPCapabilities` |
253 | `MCPResourcesCapability` | `total=False` | `resources` entry in `MCPCapabilities` |
254 | `MCPCapabilities` | `total=False` | Server capabilities advertised during `initialize` |
255 | `MCPServerInfo` | required | Server info returned in `initialize` responses |
256 | `MCPCapabilitiesResult` | required | Capability block in `initialize` result |
257 | `MCPToolCallParams` | required | Params for `tools/call` |
258
259 ### Elicitation TypedDicts (MCP 2025-11-25)
260
261 | Name | Kind | Description |
262 |------|------|-------------|
263 | `ElicitationAction` | `Literal` | `"accept"` \| `"decline"` \| `"cancel"` |
264 | `ElicitationRequest` | `total=False` | Server→client elicitation request: `mode` (`"form"` \| `"url"`), `schema` (form), `url` (URL), `message`, `request_id` |
265 | `ElicitationResponse` | required | Client→server elicitation response: `action` (`ElicitationAction`), `content` (form fields on accept) |
266 | `SessionInfo` | required | Active session summary: `session_id`, `user_id`, `created_at`, `last_active`, `pending_elicitations_count`, `sse_queue_count` |
267
268 ### DAW ↔ MCP Bridge TypedDicts
269
270 | Name | Kind | Description |
271 |------|------|-------------|
272 | `DAWToolCallMessage` | required | Message sent from MCP server to a DAW client over WebSocket |
273 | `DAWToolResponse` | `total=False` | Response sent from the DAW back after tool execution |
274
275 ### Pydantic Wire Models (FastAPI)
276
277 | Name | Description |
278 |------|-------------|
279 | `MCPPropertyDefWire` | Pydantic-safe JSON Schema property for FastAPI responses |
280 | `MCPInputSchemaWire` | Pydantic-safe tool input schema for FastAPI responses |
281 | `MCPToolDefWire` | Pydantic-safe tool definition for FastAPI route return types |
282
283 ---
284
285 ## MCP Integration Layer
286
287 **Paths:** `musehub/mcp/dispatcher.py`, `musehub/mcp/resources.py`,
288 `musehub/mcp/prompts.py`, `musehub/mcp/tools/`, `musehub/mcp/write_tools/`,
289 `musehub/mcp/session.py`, `musehub/mcp/sse.py`, `musehub/mcp/context.py`,
290 `musehub/mcp/elicitation.py`, `musehub/api/routes/mcp.py`, `musehub/mcp/stdio_server.py`
291
292 The MCP integration layer implements the full [MCP 2025-11-25 specification](https://modelcontextprotocol.io/specification/2025-11-25)
293 as a pure-Python async stack. No external MCP SDK dependency. Three HTTP endpoints
294 (`POST /mcp`, `GET /mcp`, `DELETE /mcp`) and stdio are supported.
295
296 ### Session Layer (`mcp/session.py`)
297
298 Stateful connection management for the Streamable HTTP transport.
299
300 | Type / Export | Kind | Description |
301 |---------------|------|-------------|
302 | `MCPSession` | `@dataclass` | Active session: `session_id`, `user_id`, `client_capabilities`, `pending` (elicitation Futures), `sse_queues`, `event_buffer`, `created_at`, `last_active` |
303 | `create_session(user_id, client_capabilities)` | function | Mint a new session; registers background cleanup task |
304 | `get_session(session_id)` | function | Look up by ID; returns `None` if expired |
305 | `delete_session(session_id)` | function | Terminate session; drains SSE queues; cancels pending Futures |
306 | `push_to_session(session, event_text)` | function | Broadcast SSE event to all open GET /mcp consumers |
307 | `register_sse_queue(session, last_event_id)` | async generator | Yield SSE events; replay buffer; heartbeat-friendly |
308 | `create_pending_elicitation(session, request_id)` | function | Register a `Future` for an outbound `elicitation/create` |
309 | `resolve_elicitation(session, request_id, result)` | function | Set the Future result on client response |
310 | `cancel_elicitation(session, request_id)` | function | Cancel the Future on `notifications/cancelled` |
311
312 Session TTL: 1 hour of inactivity. Background cleanup runs every 5 minutes. SSE ring buffer holds 50 events for `Last-Event-ID` replay.
313
314 ### SSE Utilities (`mcp/sse.py`)
315
316 | Export | Signature | Description |
317 |--------|-----------|-------------|
318 | `sse_event(data, *, event_id, event_type, retry_ms)` | `(JSONObject, ...) → str` | Format a JSON object as an SSE event string ending with `\n\n` |
319 | `sse_notification(method, params, *, event_id)` | `(str, ...) → str` | Format a JSON-RPC 2.0 notification as SSE |
320 | `sse_request(req_id, method, params, *, event_id)` | `(str\|int, str, ...) → str` | Format a JSON-RPC 2.0 request (server-initiated) as SSE |
321 | `sse_response(req_id, result, *, event_id)` | `(str\|int\|None, JSONObject, ...) → str` | Format a JSON-RPC 2.0 success response as SSE |
322 | `sse_heartbeat()` | `() → str` | Return the SSE heartbeat comment (`": heartbeat\n\n"`) |
323 | `heartbeat_stream(event_stream, *, interval_seconds)` | `async generator` | Interleave heartbeat comments into an event stream |
324 | `SSE_CONTENT_TYPE` | `str` | `"text/event-stream"` |
325
326 ### Tool Call Context (`mcp/context.py`)
327
328 | Type | Kind | Description |
329 |------|------|-------------|
330 | `ToolCallContext` | `@dataclass` | Passed to every tool executor; carries `user_id` and `session` |
331 | `.elicit_form(schema, message)` | `async → dict \| None` | Send form elicitation, await Future (5 min timeout); returns `content` dict on accept, `None` on decline/cancel/no-session |
332 | `.elicit_url(url, message, elicitation_id)` | `async → bool` | Send URL elicitation, await Future; returns `True` on accept |
333 | `.progress(token, value, total, label)` | `async → None` | Push `notifications/progress` SSE event; silent no-op without session |
334 | `.has_session` | `bool` | `True` if session is attached |
335 | `.has_elicitation` | `bool` | `True` if client supports form-mode elicitation |
336
337 ### Elicitation Schemas (`mcp/elicitation.py`)
338
339 | Export | Type | Description |
340 |--------|------|-------------|
341 | `SCHEMAS` | `dict[str, JSONObject]` | 5 restricted JSON Schema objects for musical form elicitation |
342 | `AVAILABLE_KEYS` | `list[str]` | 24 musical key signatures |
343 | `AVAILABLE_MOODS` | `list[str]` | 10 mood enums |
344 | `AVAILABLE_GENRES` | `list[str]` | 10 genre enums |
345 | `AVAILABLE_DAWS` | `list[str]` | 10 DAW names |
346 | `AVAILABLE_PLATFORMS` | `list[str]` | 8 streaming platform names |
347 | `AVAILABLE_DAW_CLOUDS` | `list[str]` | 5 cloud DAW / mastering service names |
348 | `build_form_elicitation(schema_key, message, *, request_id)` | function | Build form-mode elicitation params dict |
349 | `build_url_elicitation(url, message, *, elicitation_id)` | function | Build URL-mode elicitation params dict + stable ID |
350 | `oauth_connect_url(platform, elicitation_id, base_url)` | function | Build MuseHub OAuth start page URL |
351 | `daw_cloud_connect_url(service, elicitation_id, base_url)` | function | Build cloud DAW OAuth start page URL |
352
353 Schema keys: `compose_preferences`, `repo_creation`, `pr_review_focus`, `release_metadata`, `platform_connect_confirm`.
354
355 ### Tool Catalogue (`mcp/tools/`)
356
357 | Export | Type | Description |
358 |--------|------|-------------|
359 | `MUSEHUB_READ_TOOLS` | `list[MCPToolDef]` | 15 read-only tool definitions (browsing, search, inspect) |
360 | `MUSEHUB_WRITE_TOOLS` | `list[MCPToolDef]` | 12 write tool definitions (create, update, merge, star) |
361 | `MUSEHUB_ELICITATION_TOOLS` | `list[MCPToolDef]` | 5 elicitation-powered tool definitions (MCP 2025-11-25) |
362 | `MUSEHUB_TOOLS` | `list[MCPToolDef]` | Combined catalogue of all 32 `musehub_*` tools |
363 | `MUSEHUB_TOOL_NAMES` | `set[str]` | All tool name strings for fast routing |
364 | `MUSEHUB_WRITE_TOOL_NAMES` | `set[str]` | Write + interactive names; presence triggers JWT auth check |
365 | `MUSEHUB_ELICITATION_TOOL_NAMES` | `set[str]` | Elicitation-powered names; require session |
366 | `MCP_TOOLS` | `list[MCPToolDef]` | Full registered tool list |
367 | `TOOL_CATEGORIES` | `dict[str, str]` | Maps tool name → `"musehub-read"`, `"musehub-write"`, or `"musehub-elicitation"` |
368
369 **Read tools:** `musehub_browse_repo`, `musehub_list_branches`, `musehub_list_commits`,
370 `musehub_read_file`, `musehub_get_analysis`, `musehub_search`, `musehub_get_context`,
371 `musehub_get_commit`, `musehub_compare`, `musehub_list_issues`, `musehub_get_issue`,
372 `musehub_list_prs`, `musehub_get_pr`, `musehub_list_releases`, `musehub_search_repos`
373
374 **Write tools:** `musehub_create_repo`, `musehub_fork_repo`, `musehub_create_issue`,
375 `musehub_update_issue`, `musehub_create_issue_comment`, `musehub_create_pr`,
376 `musehub_merge_pr`, `musehub_create_pr_comment`, `musehub_submit_pr_review`,
377 `musehub_create_release`, `musehub_star_repo`, `musehub_create_label`
378
379 **Elicitation tools:** `musehub_compose_with_preferences`, `musehub_review_pr_interactive`,
380 `musehub_connect_streaming_platform`, `musehub_connect_daw_cloud`, `musehub_create_release_interactive`
381
382 ### Resource Catalogue (`mcp/resources.py`)
383
384 TypedDicts for the `musehub://` URI scheme.
385
386 | Name | Kind | Description |
387 |------|------|-------------|
388 | `MCPResource` | `TypedDict total=False` | Static resource entry: `uri`, `name`, `description`, `mimeType` |
389 | `MCPResourceTemplate` | `TypedDict total=False` | RFC 6570 URI template entry: `uriTemplate`, `name`, `description`, `mimeType` |
390 | `MCPResourceContent` | `TypedDict` | Content block returned by `resources/read`: `uri`, `mimeType`, `text` |
391
392 | Export | Type | Description |
393 |--------|------|-------------|
394 | `STATIC_RESOURCES` | `list[MCPResource]` | 5 static URIs (`trending`, `me`, `me/notifications`, `me/starred`, `me/feed`) |
395 | `RESOURCE_TEMPLATES` | `list[MCPResourceTemplate]` | 15 RFC 6570 URI templates for repos, issues, PRs, releases, users |
396 | `read_resource(uri, user_id)` | `async (str, str \| None) → dict[str, JSONValue]` | Dispatches a `musehub://` URI to the appropriate handler |
397
398 ### Prompt Catalogue (`mcp/prompts.py`)
399
400 TypedDicts for workflow-oriented agent guidance.
401
402 | Name | Kind | Description |
403 |------|------|-------------|
404 | `MCPPromptArgument` | `TypedDict total=False` | Named argument for a prompt: `name` (required), `description`, `required` |
405 | `MCPPromptDef` | `TypedDict total=False` | Prompt definition: `name` (required), `description` (required), `arguments` |
406 | `MCPPromptMessageContent` | `TypedDict` | Content inside a prompt message: `type`, `text` |
407 | `MCPPromptMessage` | `TypedDict` | A single prompt message: `role`, `content` |
408 | `MCPPromptResult` | `TypedDict` | Prompt assembly result: `description`, `messages` |
409
410 | Export | Type | Description |
411 |--------|------|-------------|
412 | `PROMPT_CATALOGUE` | `list[MCPPromptDef]` | 8 workflow prompts |
413 | `PROMPT_NAMES` | `set[str]` | All prompt name strings for fast lookup |
414 | `get_prompt(name, arguments)` | `(str, dict[str, str] \| None) → MCPPromptResult \| None` | Assembles a prompt by name with optional argument substitution |
415
416 **Prompts:** `musehub/orientation`, `musehub/contribute`, `musehub/compose`,
417 `musehub/review_pr`, `musehub/issue_triage`, `musehub/release_prep`,
418 `musehub/onboard` _(MCP 2025-11-25 elicitation-aware)_,
419 `musehub/release_to_world` _(MCP 2025-11-25 elicitation-aware)_
420
421 ### Dispatcher (`mcp/dispatcher.py`)
422
423 The pure-Python async JSON-RPC 2.0 engine. Receives parsed request dicts,
424 routes to tools/resources/prompts, and returns JSON-RPC 2.0 response dicts.
425 `ToolCallContext` is threaded into `_dispatch` so elicitation tools get access
426 to their session.
427
428 | Export | Signature | Description |
429 |--------|-----------|-------------|
430 | `handle_request(raw, user_id, session)` | `async (JSONObject, str \| None, MCPSession \| None) → JSONObject \| None` | Handle one JSON-RPC 2.0 message; returns `None` for notifications |
431 | `handle_batch(raw, user_id, session)` | `async (list[JSONValue], str \| None, MCPSession \| None) → list[JSONObject]` | Handle a batch (array); filters out notification `None`s |
432
433 **Supported methods:**
434
435 | Method | Auth required | Description |
436 |--------|---------------|-------------|
437 | `initialize` | No | Handshake; advertises `elicitation` capability (MCP 2025-11-25) |
438 | `tools/list` | No | Returns all 32 tool definitions |
439 | `tools/call` | Write + elicitation tools only | Routes to read, write, or elicitation executor |
440 | `resources/list` | No | Returns 5 static resources |
441 | `resources/templates/list` | No | Returns 15 URI templates |
442 | `resources/read` | No (visibility checked) | Reads a `musehub://` URI |
443 | `prompts/list` | No | Returns 8 prompt definitions |
444 | `prompts/get` | No | Assembles a named prompt |
445 | `notifications/cancelled` | No | Cancels a pending elicitation Future |
446 | `notifications/elicitation/complete` | No | Resolves a URL-mode elicitation Future |
447
448 ### HTTP Transport (`api/routes/mcp.py`)
449
450 Full MCP 2025-11-25 Streamable HTTP transport.
451
452 | Endpoint | Description |
453 |----------|-------------|
454 | `POST /mcp` | Accepts JSON or JSON array. Returns `application/json` or `text/event-stream` for elicitation tools. Validates `Origin`, `Mcp-Session-Id`, and optional `MCP-Protocol-Version`. |
455 | `GET /mcp` | Persistent SSE push channel. Requires `Mcp-Session-Id`. Supports `Last-Event-ID` replay from a 50-event ring buffer. |
456 | `DELETE /mcp` | Terminates session: drains SSE queues, cancels pending elicitation Futures. Returns `200 OK`. |
457
458 ### Elicitation UI Routes (`api/routes/musehub/ui_mcp_elicitation.py`)
459
460 Browser-facing pages for URL-mode OAuth elicitation flows.
461
462 | Route | Description |
463 |-------|-------------|
464 | `GET /musehub/ui/mcp/connect/{platform_slug}` | OAuth start page for streaming platforms; renders `mcp/elicitation_connect.html` with platform context and permissions. |
465 | `GET /musehub/ui/mcp/connect/daw/{service_slug}` | OAuth start page for cloud DAW services. |
466 | `GET /musehub/ui/mcp/elicitation/{elicitation_id}/callback` | OAuth redirect target; resolves elicitation Future; renders `mcp/elicitation_callback.html` (auto-close). |
467
468 **Templates:**
469 - `musehub/templates/mcp/elicitation_connect.html` — OAuth consent / connect page
470 - `musehub/templates/mcp/elicitation_callback.html` — Post-OAuth result page (auto-close tab)
471
472 ### Stdio Transport (`mcp/stdio_server.py`)
473
474 Line-delimited JSON-RPC over `stdin` / `stdout` for local development and
475 Cursor IDE integration. Registered in `.cursor/mcp.json` as:
476
477 ```json
478 {
479 "mcpServers": {
480 "musehub": {
481 "command": "python",
482 "args": ["-m", "musehub.mcp.stdio_server"],
483 "cwd": "/Users/gabriel/musehub"
484 }
485 }
486 }
487 ```
488
489 ---
490
491 ## Pydantic Base Types
492
493 **Path:** `musehub/contracts/pydantic_types.py`, `musehub/models/base.py`
494
495 ### `PydanticJson`
496
497 `RootModel[str | int | float | bool | None | list["PydanticJson"] | dict[str, "PydanticJson"]]`
498
499 The only safe recursive JSON field type for Pydantic models. Used wherever
500 a model field accepts an arbitrary JSON value (e.g. tool call parameters or
501 protocol introspection schemas). Replaces `dict[str, Any]` at every Pydantic
502 boundary.
503
504 **Helpers:**
505
506 | Function | Description |
507 |----------|-------------|
508 | `wrap(v: JSONValue) -> PydanticJson` | Convert a `JSONValue` to a `PydanticJson` instance |
509 | `unwrap(p: PydanticJson) -> JSONValue` | Convert a `PydanticJson` back to a `JSONValue` |
510 | `wrap_dict(d: dict[str, JSONValue]) -> dict[str, PydanticJson]` | Wrap each value in a dict |
511
512 ### `CamelModel`
513
514 `BaseModel` subclass. All Pydantic API models (request bodies and response
515 types) inherit from this. Configures:
516
517 - `alias_generator = to_camel` — fields are serialised to camelCase on the wire.
518 - `populate_by_name = True` — allows snake_case names in Python code.
519 - `extra = "ignore"` — unknown fields from clients are silently dropped.
520
521 ---
522
523 ## Auth Types
524
525 **Path:** `musehub/auth/tokens.py`
526
527 ### `TokenClaims`
528
529 `TypedDict (total=False)` — Decoded JWT payload returned by `validate_access_code`.
530 `type`, `iat`, and `exp` are always present (`Required`); `sub` and `role` are
531 optional claims added by the issuer.
532
533 | Field | Type | Required |
534 |-------|------|----------|
535 | `type` | `str` | Yes |
536 | `iat` | `int` | Yes |
537 | `exp` | `int` | Yes |
538 | `sub` | `str` | No |
539 | `role` | `str` | No |
540
541 ---
542
543 ## Protocol Events
544
545 **Path:** `musehub/protocol/events.py`
546
547 Protocol events are Pydantic `CamelModel` subclasses of `MuseEvent`, which
548 provides `type`, `seq`, and `protocol_version` on every payload. MuseHub
549 defines two concrete event types — both in the MCP relay path.
550
551 ### Base Class
552
553 | Name | Kind | Description |
554 |------|------|-------------|
555 | `MuseEvent` | Pydantic (CamelModel) | Base class: `type: str`, `seq: int = -1`, `protocol_version: str` |
556
557 ### Concrete Event Types
558
559 | Event | `type` Literal | Description |
560 |-------|---------------|-------------|
561 | `MCPMessageEvent` | `"mcp.message"` | MCP tool-call message relayed over SSE; `payload: dict[str, object]` |
562 | `MCPPingEvent` | `"mcp.ping"` | MCP SSE keepalive heartbeat |
563
564 The event registry (`protocol/registry.py`) maps these two type strings to
565 their model classes and is frozen at import time.
566
567 ---
568
569 ## Protocol HTTP Responses
570
571 **Path:** `musehub/protocol/responses.py`
572
573 Pydantic response models for the four protocol introspection endpoints.
574 Fields use camelCase by declaration to match the wire format.
575
576 | Name | Route | Description |
577 |------|-------|-------------|
578 | `ProtocolInfoResponse` | `GET /protocol` | Version, hash, and registered event type list |
579 | `ProtocolEventsResponse` | `GET /protocol/events.json` | JSON Schema per event type |
580 | `ProtocolToolsResponse` | `GET /protocol/tools.json` | All registered MCP tool definitions |
581 | `ProtocolSchemaResponse` | `GET /protocol/schema.json` | Unified snapshot — version + hash + events + tools |
582
583 The protocol hash (`protocol/hash.py`) is a SHA-256 over the serialised event
584 schemas and tool schemas, computed deterministically at request time.
585
586 ---
587
588 ## API Models
589
590 **Path:** `musehub/models/musehub.py`
591
592 All are Pydantic `CamelModel` subclasses. Organized by domain feature.
593
594 ### Git / VCS
595
596 | Name | Description |
597 |------|-------------|
598 | `CommitInput` | A single commit in a push payload |
599 | `ObjectInput` | A binary object in a push payload |
600 | `PushRequest` | Body for `POST /repos/{id}/push` |
601 | `PushResponse` | Push confirmation with new branch head |
602 | `PullRequest` | Body for `POST /repos/{id}/pull` |
603 | `ObjectResponse` | Binary object returned in a pull response |
604 | `PullResponse` | Pull response — missing commits and objects |
605
606 ### Repositories
607
608 | Name | Description |
609 |------|-------------|
610 | `CreateRepoRequest` | Repository creation wizard body |
611 | `RepoResponse` | Wire representation of a MuseHub repo |
612 | `TransferOwnershipRequest` | Transfer repo to another user |
613 | `RepoListResponse` | Paginated list of repos |
614 | `RepoStatsResponse` | Aggregated commit / branch / release counts |
615
616 ### Branches
617
618 | Name | Description |
619 |------|-------------|
620 | `BranchResponse` | Branch name and head commit pointer |
621 | `BranchListResponse` | Paginated list of branches |
622 | `BranchDivergenceScores` | Five-dimensional musical divergence scores |
623 | `BranchDetailResponse` | Branch with ahead/behind counts and divergence |
624 | `BranchDetailListResponse` | List of branches with detail |
625
626 ### Commits and Tags
627
628 | Name | Description |
629 |------|-------------|
630 | `CommitResponse` | Wire representation of a pushed commit |
631 | `CommitListResponse` | Paginated list of commits |
632 | `TagResponse` | A single tag entry |
633 | `TagListResponse` | All tags grouped by namespace |
634
635 ### Issues
636
637 | Name | Description |
638 |------|-------------|
639 | `IssueCreate` | Create issue body |
640 | `IssueUpdate` | Partial update body |
641 | `IssueResponse` | Wire representation of an issue |
642 | `IssueListResponse` | Paginated list of issues |
643 | `MusicalRef` | Parsed musical context reference (e.g. `track:bass`) |
644 | `IssueCommentCreate` | Create comment body |
645 | `IssueCommentResponse` | Wire representation of a comment |
646 | `IssueCommentListResponse` | Threaded discussion on an issue |
647 | `IssueAssignRequest` | Assign or unassign a user |
648 | `IssueLabelAssignRequest` | Replace the label list on an issue |
649
650 ### Milestones
651
652 | Name | Description |
653 |------|-------------|
654 | `MilestoneCreate` | Create milestone body |
655 | `MilestoneResponse` | Wire representation of a milestone |
656 | `MilestoneListResponse` | List of milestones |
657
658 ### Pull Requests
659
660 | Name | Description |
661 |------|-------------|
662 | `PRCreate` | Create PR body |
663 | `PRResponse` | Wire representation of a pull request |
664 | `PRListResponse` | Paginated list of pull requests |
665 | `PRMergeRequest` | Merge strategy selection body |
666 | `PRMergeResponse` | Merge confirmation |
667 | `PRDiffDimensionScore` | Per-dimension musical change score |
668 | `PRDiffResponse` | Musical diff between PR branches |
669 | `PRCommentCreate` | PR review comment body (supports four targeting granularities) |
670 | `PRCommentResponse` | Wire representation of a PR review comment |
671 | `PRCommentListResponse` | Threaded list of PR comments |
672 | `PRReviewerRequest` | Request reviewers |
673 | `PRReviewCreate` | Submit a formal review (approve / request_changes / comment) |
674 | `PRReviewResponse` | Wire representation of a review |
675 | `PRReviewListResponse` | List of reviews for a PR |
676
677 ### Releases
678
679 | Name | Description |
680 |------|-------------|
681 | `ReleaseCreate` | Create release body |
682 | `ReleaseDownloadUrls` | Structured download package URLs |
683 | `ReleaseResponse` | Wire representation of a release |
684 | `ReleaseListResponse` | List of releases |
685 | `ReleaseAssetCreate` | Attach asset to release |
686 | `ReleaseAssetResponse` | Wire representation of an asset |
687 | `ReleaseAssetListResponse` | Assets for a release |
688 | `ReleaseAssetDownloadCount` | Per-asset download count |
689 | `ReleaseDownloadStatsResponse` | Download counts for all assets |
690
691 ### Profile and Social
692
693 | Name | Description |
694 |------|-------------|
695 | `ProfileUpdateRequest` | Update profile body |
696 | `ProfileRepoSummary` | Compact repo summary on a profile page |
697 | `ProfileResponse` | Full wire representation of a user profile |
698 | `ContributorCredits` | Single contributor's credit record |
699 | `CreditsResponse` | Full credits roll for a repo |
700 | `StarResponse` | Star added / removed confirmation |
701 | `ContributionDay` | One day in the contribution heatmap |
702
703 ### Discovery and Search
704
705 | Name | Description |
706 |------|-------------|
707 | `ExploreRepoResult` | Public repo card on the explore page |
708 | `ExploreResponse` | Paginated discover response |
709 | `SearchCommitMatch` | Single commit returned by an in-repo search |
710 | `SearchResponse` | Response for all four in-repo search modes |
711 | `GlobalSearchCommitMatch` | Commit match in a cross-repo search |
712 | `GlobalSearchRepoGroup` | All matches for one repo in a cross-repo search |
713 | `GlobalSearchResult` | Top-level cross-repo search response |
714
715 ### Timeline, DAG, and Analytics
716
717 | Name | Description |
718 |------|-------------|
719 | `TimelineCommitEvent` | A commit plotted on the timeline |
720 | `TimelineEmotionEvent` | Emotion-vector data point on the timeline |
721 | `TimelineSectionEvent` | Detected section change marker |
722 | `TimelineTrackEvent` | Track addition or removal event |
723 | `TimelineResponse` | Chronological musical evolution timeline |
724 | `DivergenceDimensionResponse` | Per-dimension divergence score |
725 | `DivergenceResponse` | Full musical divergence report between two branches |
726 | `CommitDiffDimensionScore` | Per-dimension change score vs parent |
727 | `CommitDiffSummaryResponse` | Multi-dimensional diff summary |
728 | `DagNode` | Single commit node in the DAG |
729 | `DagEdge` | Directed edge in the commit DAG |
730 | `DagGraphResponse` | Topologically sorted commit graph |
731
732 ### Webhooks
733
734 | Name | Description |
735 |------|-------------|
736 | `WebhookCreate` | Create webhook body |
737 | `WebhookResponse` | Wire representation of a webhook |
738 | `WebhookListResponse` | List of webhooks |
739 | `WebhookDeliveryResponse` | Single delivery attempt |
740 | `WebhookDeliveryListResponse` | Delivery history |
741 | `WebhookRedeliverResponse` | Redeliver confirmation |
742 | `PushEventPayload` | TypedDict — push event webhook payload |
743 | `IssueEventPayload` | TypedDict — issue event webhook payload |
744 | `PullRequestEventPayload` | TypedDict (total=False) — PR event webhook payload |
745 | `WebhookEventPayload` | TypeAlias — `PushEventPayload \| IssueEventPayload \| PullRequestEventPayload` |
746
747 ### Context
748
749 | Name | Description |
750 |------|-------------|
751 | `MuseHubContextCommitInfo` | Minimal commit metadata in a context document |
752 | `MuseHubContextHistoryEntry` | Ancestor commit in evolutionary history |
753 | `MuseHubContextMusicalState` | Musical state at the target commit |
754 | `MuseHubContextResponse` | Full context document for a commit |
755
756 ### Objects
757
758 | Name | Description |
759 |------|-------------|
760 | `ObjectMetaResponse` | Artifact metadata (no content) |
761 | `ObjectMetaListResponse` | List of artifact metadata |
762
763 ---
764
765 ## Database ORM Models
766
767 **Path:** `musehub/db/`
768
769 All are SQLAlchemy ORM subclasses of a declarative `Base`.
770
771 ### `db/models.py` — Auth
772
773 | Model | Table | Description |
774 |-------|-------|-------------|
775 | `User` | `muse_users` | User account; `id` = JWT `sub` claim |
776 | `AccessToken` | `muse_access_tokens` | JWT token tracking — stores SHA-256 hash, never the raw token |
777
778 ### `db/muse_cli_models.py` — Muse CLI VCS
779
780 | Model | Table | Description |
781 |-------|-------|-------------|
782 | `MuseCliObject` | `muse_objects` | Content-addressed blob |
783 | `MuseCliSnapshot` | `muse_snapshots` | Immutable snapshot manifest |
784 | `MuseCliCommit` | `muse_commits` | Versioned commit pointing to a snapshot |
785 | `MuseCliTag` | `muse_tags` | Music-semantic tag on a CLI commit |
786
787 ### `db/musehub_models.py` — MuseHub Core
788
789 | Model | Table | Description |
790 |-------|-------|-------------|
791 | `MusehubRepo` | `musehub_repos` | Remote repository with music-semantic metadata |
792 | `MusehubBranch` | `musehub_branches` | Named branch pointer |
793 | `MusehubCommit` | `musehub_commits` | Commit pushed to MuseHub |
794 | `MusehubObject` | `musehub_objects` | Content-addressed binary artifact |
795 | `MusehubMilestone` | `musehub_milestones` | Milestone grouping issues |
796 | `MusehubIssueMilestone` | `musehub_issue_milestones` | Issue ↔ Milestone join table |
797 | `MusehubIssue` | `musehub_issues` | Issue opened against a repo |
798 | `MusehubIssueComment` | `musehub_issue_comments` | Threaded issue comment |
799 | `MusehubPullRequest` | `musehub_pull_requests` | Pull request |
800 | `MusehubPRReview` | `musehub_pr_reviews` | Formal PR review submission |
801 | `MusehubPRComment` | `musehub_pr_comments` | Inline musical diff comment on a PR |
802 | `MusehubRelease` | `musehub_releases` | Published version release |
803 | `MusehubReleaseAsset` | `musehub_release_assets` | Downloadable file attachment |
804 | `MusehubProfile` | `musehub_profiles` | Public user musical portfolio |
805 | `MusehubWebhook` | `musehub_webhooks` | Registered webhook subscription |
806 | `MusehubWebhookDelivery` | `musehub_webhook_deliveries` | Single webhook delivery attempt |
807 | `MusehubStar` | `musehub_stars` | User star on a public repo |
808 | `MusehubSession` | `musehub_sessions` | Recording session record pushed from the CLI |
809 | `MusehubComment` | `musehub_comments` | Threaded comment on any repo object |
810 | `MusehubReaction` | `musehub_reactions` | Emoji reaction on a comment or target |
811 | `MusehubFollow` | `musehub_follows` | User follows user — social graph |
812 | `MusehubWatch` | `musehub_watches` | User watches a repo for notifications |
813 | `MusehubNotification` | `musehub_notifications` | Notification delivered to a user |
814 | `MusehubFork` | `musehub_forks` | Fork relationship between two repos |
815 | `MusehubViewEvent` | `musehub_view_events` | Debounced repo view (one row per visitor per day) |
816 | `MusehubDownloadEvent` | `musehub_download_events` | Artifact export download event |
817 | `MusehubRenderJob` | `musehub_render_jobs` | Async MP3/piano-roll render job tracking |
818 | `MusehubEvent` | `musehub_events` | Append-only repo activity event stream |
819
820 ### `db/musehub_collaborator_models.py`
821
822 | Model | Table | Description |
823 |-------|-------|-------------|
824 | `MusehubCollaborator` | `musehub_collaborators` | Explicit push/admin access grant for a user on a repo |
825
826 ### `db/musehub_label_models.py`
827
828 | Model | Table | Description |
829 |-------|-------|-------------|
830 | `MusehubLabel` | `musehub_labels` | Coloured label tag scoped to a repo |
831 | `MusehubIssueLabel` | `musehub_issue_labels` | Issue ↔ Label join table |
832 | `MusehubPRLabel` | `musehub_pr_labels` | PR ↔ Label join table |
833
834 ### `db/musehub_stash_models.py`
835
836 | Model | Table | Description |
837 |-------|-------|-------------|
838 | `MusehubStash` | `musehub_stash` | Named save point for uncommitted changes |
839 | `MusehubStashEntry` | `musehub_stash_entries` | Single MIDI file snapshot within a stash |
840
841 ---
842
843 ## Entity Hierarchy
844
845 ```
846 MuseHub
847
848 ├── MIDI Type Aliases (contracts/midi_types.py)
849 │ ├── MidiPitch, MidiVelocity, MidiChannel, MidiCC, MidiCCValue
850 │ ├── MidiAftertouchValue, MidiGMProgram, MidiPitchBend, MidiBPM
851 │ └── BeatPosition, BeatDuration, ArrangementBeat, ArrangementDuration, Bars
852
853 ├── JSON Types (contracts/json_types.py)
854 │ ├── Primitives: JSONScalar, JSONValue, JSONObject
855 │ ├── Region maps: RegionNotesMap, RegionCCMap, RegionPitchBendMap, RegionAftertouchMap
856 │ ├── Protocol aliases: EventJsonSchema, EventSchemaMap
857 │ └── TypedDicts: NoteDict, CCEventDict, PitchBendDict, AftertouchDict, SectionDict
858
859 ├── MCP Protocol (contracts/mcp_types.py)
860 │ ├── MCPRequest — TypedDict (total=False)
861 │ ├── MCPResponse — MCPSuccessResponse | MCPErrorResponse
862 │ ├── MCPMethodResponse — 5-way union of concrete response types
863 │ ├── MCPToolDef — TypedDict (total=False)
864 │ ├── DAWToolCallMessage — TypedDict
865 │ ├── DAWToolResponse — TypedDict (total=False)
866 │ └── MCPToolDefWire — Pydantic (FastAPI serialisation)
867
868 ├── Pydantic Base (contracts/pydantic_types.py, models/base.py)
869 │ ├── PydanticJson — RootModel: recursive JSON, safe for Pydantic fields
870 │ └── CamelModel — BaseModel: camelCase alias, populate_by_name, extra=ignore
871
872 ├── Auth (auth/tokens.py)
873 │ └── TokenClaims — TypedDict (total=False): decoded JWT payload
874
875 ├── Protocol Events (protocol/events.py)
876 │ ├── MuseEvent — Pydantic base: type + seq + protocol_version
877 │ ├── MCPMessageEvent — type: "mcp.message"; payload: dict[str, object]
878 │ └── MCPPingEvent — type: "mcp.ping" (keepalive)
879
880 ├── Protocol Introspection (protocol/)
881 │ ├── EVENT_REGISTRY — dict[str, type[MuseEvent]] (2 entries)
882 │ ├── compute_protocol_hash() — SHA-256 over events + tools schemas
883 │ ├── ProtocolInfoResponse — GET /protocol
884 │ ├── ProtocolEventsResponse — GET /protocol/events.json
885 │ ├── ProtocolToolsResponse — GET /protocol/tools.json
886 │ └── ProtocolSchemaResponse — GET /protocol/schema.json
887
888 ├── MCP Integration Layer (mcp/)
889 │ ├── Tools (mcp/tools/)
890 │ │ ├── MUSEHUB_READ_TOOLS — 15 read tool definitions
891 │ │ ├── MUSEHUB_WRITE_TOOLS — 12 write tool definitions
892 │ │ ├── MUSEHUB_TOOLS — combined 27-tool catalogue
893 │ │ ├── MUSEHUB_TOOL_NAMES — set[str] for routing
894 │ │ ├── MUSEHUB_WRITE_TOOL_NAMES — set[str] auth-gated writes
895 │ │ ├── MCP_TOOLS — registered list (alias)
896 │ │ └── TOOL_CATEGORIES — dict[str, str] tool → category
897 │ ├── Resources (mcp/resources.py)
898 │ │ ├── MCPResource — TypedDict (total=False): static resource
899 │ │ ├── MCPResourceTemplate — TypedDict (total=False): RFC 6570 template
900 │ │ ├── MCPResourceContent — TypedDict: read result content block
901 │ │ ├── STATIC_RESOURCES — 5 static musehub:// URIs
902 │ │ ├── RESOURCE_TEMPLATES — 15 RFC 6570 URI templates
903 │ │ └── read_resource() — async URI dispatcher
904 │ ├── Prompts (mcp/prompts.py)
905 │ │ ├── MCPPromptArgument — TypedDict (total=False): prompt argument
906 │ │ ├── MCPPromptDef — TypedDict (total=False): prompt definition
907 │ │ ├── MCPPromptMessage — TypedDict: role + content
908 │ │ ├── MCPPromptResult — TypedDict: description + messages
909 │ │ ├── PROMPT_CATALOGUE — 6 workflow prompts
910 │ │ ├── PROMPT_NAMES — set[str] for lookup
911 │ │ └── get_prompt() — assembler with argument substitution
912 │ ├── Dispatcher (mcp/dispatcher.py)
913 │ │ ├── handle_request() — async JSON-RPC 2.0 single request
914 │ │ └── handle_batch() — async JSON-RPC 2.0 batch
915 │ ├── HTTP Transport (api/routes/mcp.py)
916 │ │ └── POST /mcp — HTTP Streamable, JWT auth, batch support
917 │ └── Stdio Transport (mcp/stdio_server.py)
918 │ └── line-delimited JSON-RPC over stdin/stdout
919
920 ├── API Models (models/musehub.py) ~98 Pydantic models
921 │ ├── VCS: PushRequest, PullResponse, …
922 │ ├── Repos: CreateRepoRequest, RepoResponse, …
923 │ ├── Issues: IssueCreate, IssueResponse, IssueCommentResponse, …
924 │ ├── Pull Requests: PRCreate, PRResponse, PRDiffResponse, …
925 │ ├── Releases: ReleaseCreate, ReleaseResponse, ReleaseAssetResponse, …
926 │ ├── Profile + Social: ProfileResponse, StarResponse, …
927 │ ├── Discovery: ExploreResponse, SearchResponse, GlobalSearchResult, …
928 │ ├── Timeline + DAG: TimelineResponse, DagGraphResponse, …
929 │ └── Webhooks: WebhookCreate, WebhookDeliveryResponse, …
930
931 └── Database ORM (db/) 37 SQLAlchemy models
932 ├── Auth: User, AccessToken
933 ├── CLI VCS: MuseCliObject, MuseCliSnapshot, MuseCliCommit, MuseCliTag
934 ├── Hub Core: MusehubRepo, MusehubBranch, MusehubCommit, MusehubObject, …
935 ├── Social: MusehubStar, MusehubFollow, MusehubWatch, MusehubNotification, …
936 ├── Labels: MusehubLabel, MusehubIssueLabel, MusehubPRLabel
937 └── Stash: MusehubStash, MusehubStashEntry
938 ```
939
940 ---
941
942 ## Entity Graphs (Mermaid)
943
944 Arrow conventions:
945 - `*--` composition (owns, lifecycle-coupled)
946 - `-->` association (references)
947 - `..>` dependency (uses / produces)
948 - `..|>` implements / extends
949
950 ---
951
952 ### Diagram 1 — MIDI Note and Event Wire Types
953
954 The core typed shapes for MIDI data. `NoteDict` is dual-keyed (camelCase wire
955 + snake_case storage). The region maps alias these into domain containers.
956
957 ```mermaid
958 classDiagram
959 class NoteDict {
960 <<TypedDict total=False>>
961 pitch : MidiPitch
962 velocity : MidiVelocity
963 channel : MidiChannel
964 startBeat : BeatPosition
965 durationBeats : BeatDuration
966 noteId : str
967 trackId : str
968 regionId : str
969 start_beat : BeatPosition
970 duration_beats : BeatDuration
971 note_id : str
972 track_id : str
973 region_id : str
974 layer : str
975 }
976 class CCEventDict {
977 <<TypedDict>>
978 cc : MidiCC
979 beat : BeatPosition
980 value : MidiCCValue
981 }
982 class PitchBendDict {
983 <<TypedDict>>
984 beat : BeatPosition
985 value : MidiPitchBend
986 }
987 class AftertouchDict {
988 <<TypedDict total=False>>
989 beat : BeatPosition (Required)
990 value : MidiAftertouchValue (Required)
991 pitch : MidiPitch
992 }
993 class SectionDict {
994 <<TypedDict total=False>>
995 name : str
996 start_beat : float
997 length_beats : float
998 description : str
999 per_track_description : dict~str, str~
1000 }
1001 class RegionNotesMap {
1002 <<TypeAlias>>
1003 dict~str, list~NoteDict~~
1004 }
1005 class RegionCCMap {
1006 <<TypeAlias>>
1007 dict~str, list~CCEventDict~~
1008 }
1009 class RegionPitchBendMap {
1010 <<TypeAlias>>
1011 dict~str, list~PitchBendDict~~
1012 }
1013 class RegionAftertouchMap {
1014 <<TypeAlias>>
1015 dict~str, list~AftertouchDict~~
1016 }
1017
1018 RegionNotesMap ..> NoteDict : list element
1019 RegionCCMap ..> CCEventDict : list element
1020 RegionPitchBendMap ..> PitchBendDict : list element
1021 RegionAftertouchMap ..> AftertouchDict : list element
1022 ```
1023
1024 ---
1025
1026 ### Diagram 2 — MCP Protocol Event Types
1027
1028 The two concrete events MuseHub relays over SSE — both in the MCP path.
1029 `MuseEvent` is the typed base; the event registry maps type strings to
1030 model classes.
1031
1032 ```mermaid
1033 classDiagram
1034 class MuseEvent {
1035 <<Pydantic CamelModel>>
1036 +type : str
1037 +seq : int = -1
1038 +protocol_version : str
1039 extra = "forbid"
1040 }
1041 class MCPMessageEvent {
1042 <<Pydantic CamelModel>>
1043 +type : Literal~"mcp.message"~
1044 +payload : dict~str, object~
1045 }
1046 class MCPPingEvent {
1047 <<Pydantic CamelModel>>
1048 +type : Literal~"mcp.ping"~
1049 }
1050 class EVENT_REGISTRY {
1051 <<dict frozen at import>>
1052 "mcp.message" : MCPMessageEvent
1053 "mcp.ping" : MCPPingEvent
1054 }
1055
1056 MCPMessageEvent --|> MuseEvent
1057 MCPPingEvent --|> MuseEvent
1058 EVENT_REGISTRY ..> MCPMessageEvent : registers
1059 EVENT_REGISTRY ..> MCPPingEvent : registers
1060 ```
1061
1062 ---
1063
1064 ### Diagram 3 — Protocol Introspection
1065
1066 The hash computation pipeline and the four HTTP response types it drives.
1067
1068 ```mermaid
1069 classDiagram
1070 class ProtocolInfoResponse {
1071 <<Pydantic BaseModel>>
1072 +protocolVersion : str
1073 +protocolHash : str
1074 +eventTypes : list~str~
1075 +eventCount : int
1076 }
1077 class ProtocolEventsResponse {
1078 <<Pydantic BaseModel>>
1079 +protocolVersion : str
1080 +events : dict~str, PydanticJson~
1081 }
1082 class ProtocolToolsResponse {
1083 <<Pydantic BaseModel>>
1084 +protocolVersion : str
1085 +tools : list~MCPToolDefWire~
1086 +toolCount : int
1087 }
1088 class ProtocolSchemaResponse {
1089 <<Pydantic BaseModel>>
1090 +protocolVersion : str
1091 +protocolHash : str
1092 +events : dict~str, PydanticJson~
1093 +tools : list~MCPToolDefWire~
1094 +toolCount : int
1095 +eventCount : int
1096 }
1097 class MCPToolDefWire {
1098 <<Pydantic BaseModel>>
1099 +name : str
1100 +description : str
1101 +inputSchema : MCPInputSchemaWire
1102 }
1103
1104 ProtocolSchemaResponse ..> MCPToolDefWire : tools list
1105 ProtocolToolsResponse ..> MCPToolDefWire : tools list
1106 ProtocolSchemaResponse ..> ProtocolEventsResponse : events subset
1107 ProtocolSchemaResponse ..> ProtocolToolsResponse : tools subset
1108 ```
1109
1110 ---
1111
1112 ### Diagram 4 — MCP Tool Routing
1113
1114 How a tool name flows from an incoming request through the server routing layer
1115 to the executor and back as a `ToolCallResult`.
1116
1117 ```mermaid
1118 classDiagram
1119 class MuseMCPServer {
1120 +call_tool(name: str, params: dict) ToolCallResult
1121 -_execute_musehub_tool(name, params) MusehubToolResult
1122 -_build_result(result: MusehubToolResult) ToolCallResult
1123 }
1124 class ToolCallResult {
1125 <<dataclass>>
1126 +success : bool
1127 +is_error : bool
1128 +bad_request : bool
1129 +content : list~dict~str, str~~
1130 }
1131 class MusehubToolResult {
1132 <<dataclass frozen>>
1133 +ok : bool
1134 +data : dict~str, JSONValue~
1135 +error_code : MusehubErrorCode | None
1136 +error_message : str | None
1137 }
1138 class MusehubErrorCode {
1139 <<Literal>>
1140 "not_found"
1141 "invalid_dimension"
1142 "invalid_mode"
1143 "db_unavailable"
1144 }
1145 class MUSEHUB_READ_TOOLS {
1146 <<list of MCPToolDef — 15 tools>>
1147 musehub_browse_repo · musehub_list_branches
1148 musehub_list_commits · musehub_read_file
1149 musehub_get_analysis · musehub_search
1150 musehub_get_context · musehub_get_commit
1151 musehub_compare · musehub_list_issues
1152 musehub_get_issue · musehub_list_prs
1153 musehub_get_pr · musehub_list_releases
1154 musehub_search_repos
1155 }
1156 class MUSEHUB_WRITE_TOOLS {
1157 <<list of MCPToolDef — 12 tools>>
1158 musehub_create_repo · musehub_fork_repo
1159 musehub_create_issue · musehub_update_issue
1160 musehub_create_issue_comment · musehub_create_pr
1161 musehub_merge_pr · musehub_create_pr_comment
1162 musehub_submit_pr_review · musehub_create_release
1163 musehub_star_repo · musehub_create_label
1164 }
1165 class MCPDispatcher {
1166 +handle_request(raw, user_id) JSONObject | None
1167 +handle_batch(raw, user_id) list~JSONObject~
1168 -initialize() MCPSuccessResponse
1169 -tools_list() MCPSuccessResponse
1170 -tools_call(name, args, user_id) MCPSuccessResponse
1171 -resources_read(uri, user_id) MCPSuccessResponse
1172 -prompts_get(name, args) MCPSuccessResponse
1173 }
1174
1175 MCPDispatcher --> ToolCallResult : returns
1176 MCPDispatcher ..> MusehubToolResult : executor produces
1177 MusehubToolResult --> MusehubErrorCode : error_code
1178 MCPDispatcher ..> MUSEHUB_READ_TOOLS : routes read calls
1179 MCPDispatcher ..> MUSEHUB_WRITE_TOOLS : routes write calls (auth required)
1180 ```
1181
1182 ---
1183
1184 ### Diagram 5 — MCP JSON-RPC Wire Types
1185
1186 The full JSON-RPC 2.0 type hierarchy used by the MCP HTTP adapter.
1187
1188 ```mermaid
1189 classDiagram
1190 class MCPRequest {
1191 <<TypedDict total=False>>
1192 +jsonrpc : str
1193 +id : str | int | None
1194 +method : str
1195 +params : dict~str, object~
1196 }
1197 class MCPSuccessResponse {
1198 <<TypedDict>>
1199 +jsonrpc : str
1200 +id : str | int | None
1201 +result : object
1202 }
1203 class MCPErrorDetail {
1204 <<TypedDict total=False>>
1205 +code : int
1206 +message : str
1207 +data : object
1208 }
1209 class MCPErrorResponse {
1210 <<TypedDict>>
1211 +jsonrpc : str
1212 +id : str | int | None
1213 +error : MCPErrorDetail
1214 }
1215 class MCPToolDef {
1216 <<TypedDict total=False>>
1217 +name : str
1218 +description : str
1219 +inputSchema : MCPInputSchema
1220 +server_side : bool
1221 }
1222 class MCPContentBlock {
1223 <<TypedDict>>
1224 +type : str
1225 +text : str
1226 }
1227 class MCPCallResult {
1228 <<TypedDict total=False>>
1229 +content : list~MCPContentBlock~
1230 +isError : bool
1231 }
1232
1233 MCPErrorResponse *-- MCPErrorDetail : error
1234 MCPCallResult *-- MCPContentBlock : content
1235 MCPToolDef ..> MCPContentBlock : tools/call returns
1236 ```
1237
1238 ---
1239
1240 ### Diagram 6 — Auth and JWT
1241
1242 Token lifecycle from issuance through validation to the decoded `TokenClaims`.
1243
1244 ```mermaid
1245 classDiagram
1246 class TokenClaims {
1247 <<TypedDict total=False>>
1248 +type : str (Required)
1249 +iat : int (Required)
1250 +exp : int (Required)
1251 +sub : str
1252 +role : str
1253 }
1254 class AccessToken {
1255 <<SQLAlchemy ORM>>
1256 +id : int
1257 +token_hash : str
1258 +user_id : str
1259 +created_at : datetime
1260 +expires_at : datetime
1261 +revoked : bool
1262 }
1263 class User {
1264 <<SQLAlchemy ORM>>
1265 +id : str (JWT sub)
1266 +username : str
1267 +email : str
1268 +created_at : datetime
1269 }
1270
1271 AccessToken --> User : user_id FK
1272 TokenClaims ..> User : sub maps to User.id
1273 TokenClaims ..> AccessToken : validated against token_hash
1274 ```
1275
1276 ---
1277
1278 ### Diagram 7 — MuseHub Repository Object Graph
1279
1280 The core VCS entity relationships in the database.
1281
1282 ```mermaid
1283 classDiagram
1284 class MusehubRepo {
1285 <<SQLAlchemy ORM>>
1286 +id : str
1287 +name : str
1288 +owner_id : str
1289 +description : str
1290 +is_public : bool
1291 +created_at : datetime
1292 }
1293 class MusehubBranch {
1294 <<SQLAlchemy ORM>>
1295 +id : str
1296 +repo_id : str (FK)
1297 +name : str
1298 +head_commit_id : str
1299 }
1300 class MusehubCommit {
1301 <<SQLAlchemy ORM>>
1302 +id : str
1303 +repo_id : str (FK)
1304 +branch_id : str (FK)
1305 +snapshot_id : str
1306 +message : str
1307 +committed_at : datetime
1308 }
1309 class MusehubObject {
1310 <<SQLAlchemy ORM>>
1311 +id : str
1312 +repo_id : str (FK)
1313 +commit_id : str (FK)
1314 +path : str
1315 +content_hash : str
1316 +mime_type : str
1317 }
1318 class MusehubIssue {
1319 <<SQLAlchemy ORM>>
1320 +id : str
1321 +repo_id : str (FK)
1322 +title : str
1323 +state : str
1324 +created_at : datetime
1325 }
1326 class MusehubPullRequest {
1327 <<SQLAlchemy ORM>>
1328 +id : str
1329 +repo_id : str (FK)
1330 +source_branch : str
1331 +target_branch : str
1332 +state : str
1333 }
1334 class MusehubRelease {
1335 <<SQLAlchemy ORM>>
1336 +id : str
1337 +repo_id : str (FK)
1338 +tag : str
1339 +title : str
1340 +created_at : datetime
1341 }
1342
1343 MusehubRepo *-- MusehubBranch : branches
1344 MusehubRepo *-- MusehubCommit : commits
1345 MusehubRepo *-- MusehubObject : objects
1346 MusehubRepo *-- MusehubIssue : issues
1347 MusehubRepo *-- MusehubPullRequest : pull requests
1348 MusehubRepo *-- MusehubRelease : releases
1349 MusehubBranch --> MusehubCommit : head_commit_id
1350 MusehubCommit --> MusehubObject : objects
1351 ```
1352
1353 ---
1354
1355 ### Diagram 8 — Social and Discovery Graph
1356
1357 User-to-user and user-to-repo relationships powering the social feed and
1358 discovery pages.
1359
1360 ```mermaid
1361 classDiagram
1362 class User {
1363 <<SQLAlchemy ORM>>
1364 +id : str
1365 +username : str
1366 }
1367 class MusehubProfile {
1368 <<SQLAlchemy ORM>>
1369 +user_id : str (FK)
1370 +display_name : str
1371 +bio : str
1372 }
1373 class MusehubStar {
1374 <<SQLAlchemy ORM>>
1375 +user_id : str (FK)
1376 +repo_id : str (FK)
1377 +starred_at : datetime
1378 }
1379 class MusehubFollow {
1380 <<SQLAlchemy ORM>>
1381 +follower_id : str (FK)
1382 +followee_id : str (FK)
1383 +created_at : datetime
1384 }
1385 class MusehubWatch {
1386 <<SQLAlchemy ORM>>
1387 +user_id : str (FK)
1388 +repo_id : str (FK)
1389 }
1390 class MusehubNotification {
1391 <<SQLAlchemy ORM>>
1392 +id : str
1393 +user_id : str (FK)
1394 +event_type : str
1395 +read : bool
1396 }
1397 class MusehubRepo {
1398 <<SQLAlchemy ORM>>
1399 +id : str
1400 +name : str
1401 }
1402
1403 User *-- MusehubProfile : profile
1404 User ..> MusehubStar : stars
1405 User ..> MusehubFollow : follows / followed_by
1406 User ..> MusehubWatch : watches
1407 User ..> MusehubNotification : receives
1408 MusehubStar --> MusehubRepo : repo_id
1409 MusehubWatch --> MusehubRepo : repo_id
1410 ```
1411
1412 ---
1413
1414 ### Diagram 9 — Full Entity Overview
1415
1416 All named layers and the dependency flow between them.
1417
1418 ```mermaid
1419 classDiagram
1420 class MIDIAliases {
1421 <<contracts/midi_types.py>>
1422 MidiPitch · MidiVelocity · BeatPosition · BeatDuration · …
1423 }
1424 class JSONTypes {
1425 <<contracts/json_types.py>>
1426 NoteDict · CCEventDict · PitchBendDict · AftertouchDict · SectionDict
1427 JSONValue · JSONObject · RegionNotesMap · EventJsonSchema
1428 }
1429 class MCPTypes {
1430 <<contracts/mcp_types.py>>
1431 MCPRequest · MCPResponse · MCPToolDef · DAWToolCallMessage
1432 MCPToolDefWire (Pydantic)
1433 }
1434 class PydanticBase {
1435 <<contracts/pydantic_types.py + models/base.py>>
1436 PydanticJson (RootModel)
1437 CamelModel (BaseModel)
1438 }
1439 class AuthTypes {
1440 <<auth/tokens.py>>
1441 TokenClaims (TypedDict)
1442 }
1443 class ProtocolEvents {
1444 <<protocol/events.py>>
1445 MuseEvent (base)
1446 MCPMessageEvent · MCPPingEvent
1447 }
1448 class ProtocolResponses {
1449 <<protocol/responses.py>>
1450 ProtocolInfoResponse
1451 ProtocolEventsResponse
1452 ProtocolToolsResponse
1453 ProtocolSchemaResponse
1454 }
1455 class MCPIntegration {
1456 <<mcp/>>
1457 27 tools (15 read + 12 write)
1458 20 resources (5 static + 15 templates)
1459 6 workflow prompts
1460 MCPDispatcher · handle_request · handle_batch
1461 HTTP transport POST /mcp
1462 stdio transport
1463 }
1464 class APIModels {
1465 <<models/musehub.py>>
1466 ~98 Pydantic CamelModel subclasses
1467 VCS · Repos · Issues · PRs · Releases · Social · Search
1468 }
1469 class DatabaseORM {
1470 <<db/>>
1471 37 SQLAlchemy models
1472 User · AccessToken · MusehubRepo · MusehubBranch · MusehubCommit · …
1473 }
1474
1475 JSONTypes ..> MIDIAliases : constrained int/float aliases
1476 MCPTypes ..> PydanticBase : MCPToolDefWire extends CamelModel
1477 ProtocolEvents ..> PydanticBase : MuseEvent extends CamelModel
1478 ProtocolResponses ..> PydanticBase : all extend BaseModel
1479 ProtocolResponses ..> MCPTypes : MCPToolDefWire in tools fields
1480 MCPIntegration ..> MCPTypes : tool defs match MCPToolDef shape
1481 MCPIntegration ..> JSONTypes : MusehubToolResult.data uses JSONValue
1482 MCPIntegration ..> AuthTypes : JWT → user_id for write tools
1483 MCPIntegration ..> DatabaseORM : executors query DB via AsyncSessionLocal
1484 APIModels ..> PydanticBase : all extend CamelModel
1485 AuthTypes ..> DatabaseORM : TokenClaims.sub → User.id
1486 ```
1487
1488 ---
1489
1490 ### Diagram 10 — MCP Transport and Resource Architecture
1491
1492 The full request path from an MCP client through transports, dispatcher,
1493 and executors to the database and back.
1494
1495 ```mermaid
1496 classDiagram
1497 class HTTPTransport {
1498 <<api/routes/mcp.py — MCP 2025-11-25>>
1499 +POST /mcp JSON | SSE stream
1500 +GET /mcp SSE push channel
1501 +DELETE /mcp session termination
1502 +JWT auth (Authorization: Bearer)
1503 +Mcp-Session-Id header
1504 +Origin validation
1505 +batch support (array body)
1506 +202 for notifications
1507 }
1508 class StdioTransport {
1509 <<mcp/stdio_server.py>>
1510 +stdin line reader
1511 +stdout JSON-RPC responses
1512 +Cursor IDE integration
1513 }
1514 class SessionStore {
1515 <<mcp/session.py>>
1516 +create_session(user_id, caps)
1517 +get_session(session_id)
1518 +delete_session(session_id)
1519 +push_to_session(session, event)
1520 +register_sse_queue(session, last_event_id)
1521 +create_pending_elicitation(session, id)
1522 +resolve_elicitation(session, id, result)
1523 +cancel_elicitation(session, id)
1524 }
1525 class MCPDispatcher {
1526 <<mcp/dispatcher.py — 2025-11-25>>
1527 +handle_request(raw, user_id, session) JSONObject|None
1528 +handle_batch(raw, user_id, session) list~JSONObject~
1529 -initialize() elicitation capability
1530 -tools_list() 32 tools
1531 -tools_call(name, args, ctx)
1532 -resources_read(uri, user_id)
1533 -prompts_get(name, args)
1534 -notifications_cancelled()
1535 -notifications_elicitation_complete()
1536 }
1537 class ToolCallContext {
1538 <<mcp/context.py>>
1539 +user_id : str | None
1540 +session : MCPSession | None
1541 +has_session : bool
1542 +has_elicitation : bool
1543 +elicit_form(schema, message) dict|None
1544 +elicit_url(url, message, id) bool
1545 +progress(token, value, total, label)
1546 }
1547 class ReadExecutors {
1548 <<mcp/services/musehub_mcp_executor.py>>
1549 execute_browse_repo()
1550 execute_list_branches()
1551 execute_list_commits()
1552 execute_read_file()
1553 execute_get_analysis()
1554 execute_search()
1555 execute_get_context()
1556 execute_get_commit()
1557 execute_compare()
1558 execute_list_issues()
1559 execute_get_issue()
1560 execute_list_prs()
1561 execute_get_pr()
1562 execute_list_releases()
1563 execute_search_repos()
1564 }
1565 class WriteExecutors {
1566 <<mcp/write_tools/>>
1567 repos: execute_create_repo() execute_fork_repo()
1568 issues: execute_create_issue() execute_update_issue() execute_create_issue_comment()
1569 pulls: execute_create_pr() execute_merge_pr() execute_create_pr_comment() execute_submit_pr_review()
1570 releases: execute_create_release()
1571 social: execute_star_repo() execute_create_label()
1572 }
1573 class ElicitationExecutors {
1574 <<mcp/write_tools/elicitation_tools.py — MCP 2025-11-25>>
1575 execute_compose_with_preferences()
1576 execute_review_pr_interactive()
1577 execute_connect_streaming_platform()
1578 execute_connect_daw_cloud()
1579 execute_create_release_interactive()
1580 }
1581 class ResourceHandlers {
1582 <<mcp/resources.py>>
1583 read_resource(uri, user_id)
1584 STATIC: trending · me · me/notifications · me/starred · me/feed
1585 TEMPLATED: repos/{owner}/{slug} + 14 sub-resources
1586 users/{username}
1587 }
1588 class PromptAssembler {
1589 <<mcp/prompts.py>>
1590 get_prompt(name, arguments)
1591 orientation · contribute · compose
1592 review_pr · issue_triage · release_prep
1593 onboard · release_to_world (2025-11-25)
1594 }
1595 class MusehubToolResult {
1596 <<dataclass frozen>>
1597 +ok : bool
1598 +data : dict~str, JSONValue~
1599 +error_code : MusehubErrorCode | None
1600 +error_message : str | None
1601 }
1602
1603 HTTPTransport ..> SessionStore : Mcp-Session-Id lifecycle
1604 HTTPTransport ..> MCPDispatcher : delegates (with session)
1605 StdioTransport ..> MCPDispatcher : delegates (no session)
1606 MCPDispatcher ..> ToolCallContext : threads into tool calls
1607 MCPDispatcher ..> ReadExecutors : tools/call (read)
1608 MCPDispatcher ..> WriteExecutors : tools/call (write, auth required)
1609 MCPDispatcher ..> ElicitationExecutors : tools/call (session + auth required)
1610 ElicitationExecutors ..> ToolCallContext : uses for elicitation & progress
1611 ToolCallContext ..> SessionStore : pushes SSE events, resolves Futures
1612 MCPDispatcher ..> ResourceHandlers : resources/read
1613 MCPDispatcher ..> PromptAssembler : prompts/get
1614 ReadExecutors --> MusehubToolResult : returns
1615 WriteExecutors --> MusehubToolResult : returns
1616 ```