com.eurorackref/eurorack
https://eurorackref.com/mcpskills: {'id': 'get_module', 'name': 'get_module', 'description': 'Return the full citation-anchored specification for one Eurorack module by id.\n\nUse this when the user names a specific module and you want its specs (HP, power, jacks, parameters), capabilities (envelope, quantizer, logic, etc.), or firmware history.\n\nThe typed prose fields (jack/parameter/mode descriptions) are paraphrased summaries; manual_outline → get_manual_chunk give the verbatim manual prose to quote against. How much to quote and overall answer shape live in SKILL.md (the "Answer shape" section + §8 citation) — this description is the data contract.\n\n## Provenance fields\n\nEvery typed row in the response — capabilities[], jacks[], parameters[], modes[], firmware_versions[], plus nested zones/assignments/tracking — carries a source_id pointing at the source the claim was extracted from. Cross-reference list_references(module_id=...) for the source title and canonical_url.\n\nThe typed prose fields — jacks[].description, parameters[].behavior, modes[].description, capabilities[].notes, firmware_versions[].notes — are extractor-synthesized summaries grounded in the manual, NOT verbatim quotes. Treat them as the corpus\'s stated claim about the field; they\'re authoritative for what the field *does*, but they are not direct manual text. For verbatim quotation in your answer, always pull the actual prose via get_manual_chunk(chunk_id) — the description fields are the typed claim, not the source quote.\n\nmanual_outline[] bundles a lightweight outline of the module\'s manual prose — one entry per chunk with heading, source, and a ~140-char preview snippet. Always scan it before answering — for prose-shaped questions to find the relevant chunk, for spec-shaped questions to find a chunk to quote alongside the typed data. When a snippet looks relevant, call get_manual_chunk(chunk_id) to pull the full text. manual_outline_total is set ONLY when the outline was truncated for a verbose module; its absence means the returned outline is complete. When set, use search_manual to reach chunks beyond the cap.\n\nModule IDs are slug-shaped: "<manufacturer-id>/<module-slug>". For example:\n - alm-busy-circuits/pamelas-new-workout\n - make-noise/maths\n\n## Optional args — trim the payload, target the outline\n\nBy default this returns the full spec. For narrow questions you can shrink it:\n - view: "concise" returns just the id-card fields (name, manufacturer, hp, description, capabilities, production_status, replaced_by) and drops the heavy arrays — use it for triage ("which of these is the quantizer?") or when you only need to confirm what a module is. "full" (default) returns everything. Ignored when fields is set.\n - fields: array of top-level keys to include (e.g. ["jacks","parameters"]). id and _meta are always returned. Use this for a quick jacks-only or specs-only read instead of paying for character[]/common_problems[]/role_fitness[]/the full manual_outline. Takes precedence over view.\n - heading_filter: case-insensitive substring on manual_outline heading_path — e.g. "calibration" returns only outline chunks under a Calibration heading, so you skip scanning a long outline.\n - outline_offset / outline_limit: page through manual_outline (default 100 per page, hard max 250). Combined with manual_outline_total this lets you reach chunks past the cap without falling back to search_manual.\n\nReturns:\n - id, name, manufacturer { id, name }\n - hp, depth_mm\n - power: { plus_12, minus_12, plus_5 } (mA)\n - description (manufacturer\'s prose summary, citation-backed)\n - capabilities[]: functional tags with per-module realization notes (source_id per row)\n - jacks[]: inputs and outputs with voltage range, signal_type, prose description (a paraphrased summary, NOT a verbatim quote — to quote the manual, pull the source prose via get_manual_chunk), plus assignments[] for assignable jacks (destination menu — empty for fixed-function jacks). When mirrors_parameter is set, the jack mirrors that knob\'s current assignment (e.g. Pizza CTRL CV mirrors the CTRL knob). normalled_from { id, name } is set when this input has a hardware normal — i.e. when unpatched, it receives the signal at the named source jack (e.g. Multigrain GATE normalled from NEXT). null when no normal exists. V/Oct inputs may carry an optional tracking { tracking_range_octaves, tracking_quality, temperature_compensated } object — present only on jacks that have been audited for V/Oct metadata. Fields inside may be null when the source is silent on that aspect. Optional _field_absent: { <field_name>: { source_id, note } } records fields that were audited and found to be source-silent — read it before hedging: an entry under voltage_min means "the manual doesn\'t state this" (so a confident "the manual doesn\'t specify" answer is appropriate); the field being null *without* an entry means "not yet extracted" (hedge differently — recommend the user check the manual).\n - parameters[]: knobs, switches, menu settings with range, unit, behavior (paraphrased summary, NOT a verbatim quote — same as jacks[].description; quote get_manual_chunk for source text), plus zones[] (labeled regions along the control\'s travel — e.g. Swells FLOW "Sine" / "Random" halves, optionally mode-scoped) and assignments[] (destination menu for assignable knobs/menu-settings) — both empty arrays for plain controls. Modal-module params may also carry per_mode_notes (rebinding text keyed by mode_id slug, present only when the param rebinds per mode — e.g. Plaits MORPH, Swells EBB/FLOW). Same _field_absent convention as jacks[] — when default_value is null and _field_absent.default_value is present, the manual doesn\'t state a default.\n - modes[]: mode list for modal modules (Plaits, Swells, MFX) — { id, label, description, behavior_model_id, scope? }. Empty for modeless modules. Mode ids cross-reference parameters[].per_mode_notes keys and parameters[].zones[].mode_id. Optional scope is set when modes are selectable independently per member rather than module-wide — \'per-segment\' (Stages hold/ramp/step), \'per-envelope\' (Tangrams cycle/single), \'per-output\' (PNW), \'per-channel\'. Member count is carried by the corresponding enumerated parameters/jacks (e.g. Stages\' six Type Button N parameters), not duplicated on the mode rows.\n - hidden_functions[]: functions reached via a trigger other than a single labeled control — { id, trigger_type, affected_control, label, description }. trigger_type is a controlled vocabulary (\'long-press\' | \'hold\' | \'combo\' | \'double-press\' | \'power-on-hold\' | \'held-turn\') so recall/menu-diving load is countable; affected_control names the panel control the trigger acts on (null for module-global functions like hold-on-power-up calibration). Empty for modules whose controls are all directly labeled. Read this for "how do I get to X?" / menu-diving questions and when assessing how much hidden state a module carries — the same info used to live buried in parameters[].behavior prose.\n - panel_sections[]: manufacturer-named regions of the front panel (e.g. Multigrain "Dedicated Sound CV inputs" grouping GATE/NEXT/SELECT, "Morph section" grouping the MORPH knob + MORPH CV jack). Each entry has { label, description, members: [{ kind, id, name }] } where members cross-reference jacks[] / parameters[] by id. Empty for modules without manufacturer-named groupings.\n - character[]: curated subjective-character claims (vocal/aggressive/clean/gritty/lush/...) with source citations. Read this when the user asks about *sound* or *feel* rather than specs — filter choice for "carve rhythmic transients" or "warm pad voice" hinges on character, which the typed-fields surface can\'t carry. Each entry: { tag, note (prose elaboration), source_id (when archived in sources), citation_url + citation_quote (when sourced from a review/forum/video we don\'t archive per-module) }. Empty for modules that haven\'t been character-audited yet — distinguish "empty array, audit pending" from "no character worth noting." Tags are open-vocab; common starter set: vocal, aggressive, clean, gritty, acidic, lush, dark, bright, smooth, woody, formant, screaming.\n - common_problems[]: curated first-aid lore — repeatable failure modes that owners hit but the manual doesn\'t cover (calibration drift, hum, screen offset, firmware-flash brick recovery, bus-normalling caveats). Read this when the user asks "anything I should watch out for with X?" or describes a symptom matching a known module quirk. Each entry: { problem_summary (one sentence), cause (prose), fix_or_workaround (prose), confidence (\'confirmed\' | \'likely\' | \'anecdotal\'), source_id, citation_url, citation_quote }. Empty array means "no curated problems on file" — agents should NOT extrapolate to "no known problems"; the audit is opt-in per module and most modules have not been touched yet.\n - role_fitness[]: role-realization rollup — canonical techniques whose role_realizations this module fills, with the affordances it brings to that role. Use this when the user wants to know "what roles can this module play?" — e.g. Optomix → lpg role in low-pass-gate-pluck, affordances_provided=[lowpass-gate]. Each entry: { technique_id, technique_label, role_id, role_label, affordances_provided, notes }. Pair with list_techniques(filter={ module_id }) for the full role_definition + sibling realizations, or find_role_realizations(technique_id, role_id) to substitute other modules into the same role.\n - firmware_versions[]: version + release_date (may be partial: YYYY | YYYY-MM | YYYY-MM-DD) + notes (per-version changelog prose when the source provides one — e.g. "Added Smooth Random waveform type. Added Logic parameter (AND/OR/XOR)."). Use this to answer "what changed in v2?" without web search.\n - reference_url: canonical URL of the primary manual on the manufacturer site\n - audit_url: human-readable audit page on the audit site (per-claim citations)\n - production_status: "current" or "discontinued" — flag for recommendation safety\n - replaced_by: { id, name } when the module is discontinued and a successor exists; null otherwise\n - manual_outline[]: lightweight outline of the module\'s manual chunks — { chunk_id, source_id, source_type, source_title, heading_path, snippet, text_length }. Ordered by (source_id, chunk_index). When the snippet looks worth reading in full, call get_manual_chunk(chunk_id). Empty when no manual prose has been ingested yet for this module.\n - manual_outline_total: present only when manual_outline was truncated — the full count. Hit search_manual to reach the rest.\n - _meta: source_count, last_verified\n\nErrors:\n - "Module not found: <id>" if no module with that id exists.\n\nIf the user asks something the manual does not cover (e.g. subjective "is this good for percussion?"), say so explicitly — never confabulate from spec data.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'get_modules', 'name': 'get_modules', 'description': 'Batched get_module — returns `{ modules: [...], errors: [...] }` with full citation-anchored specs for up to 25 modules in one call.\n\nPrefer this over multiple get_module calls when you have a known list of modules to fetch (e.g. preparing to call draw_patch_diagram across N modules, comparing several candidates side-by-side). One round trip vs. N.\n\nArgs:\n - module_ids: array of "<manufacturer-id>/<module-slug>" strings. Up to 25 per call; duplicates are deduplicated.\n\nOptional args (apply to every module in the batch, same semantics as get_module):\n - view: "concise" returns the id-card subset (name, manufacturer, hp, description, capabilities, production_status, replaced_by) for every module and drops the heavy arrays — the cheapest way to triage a list ("which of these are LFOs?"). "full" (default) returns complete specs. Ignored when fields is set.\n - fields: top-level keys to include on each module (e.g. ["jacks","parameters"]). id and _meta are always returned. Use this when you only need a slice across N modules (e.g. just jacks for draw_patch_diagram) instead of N full specs.\n - heading_filter / outline_offset / outline_limit: narrow and paginate each module\'s manual_outline.\n\nReturns:\n - modules[]: GetModuleResponse for each id that resolved (same shape as get_module; narrowed when fields is set).\n - errors[]: { id, message } for each id that failed (e.g. unknown module). Other ids in the batch still resolve.\n\nIf you only need a single module, use get_module — same shape, one element. Need only jacks (e.g. for draw_patch_diagram)? Pass fields: ["jacks"] to skip the full specs.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'visualize_module', 'name': 'visualize_module', 'description': 'Module visualization tool. Use when the user wants to understand how a module\'s modes work, how parameters change between modes, or what a specific mode does — a visualization communicates the per-mode behavior better than prose. The host renders the result inline in the chat as an interactive visualization (mode buttons, per-mode descriptions, schematic curves); you do not need to build an artifact yourself — just call this tool.\n\nDo not use for general module specs (HP, jacks, capabilities) — call get_module instead.\n\nAfter calling, your prose can reference what the user is seeing in the visualization (e.g. "in formant mode, all three outputs become bandpass filters") rather than describing the visualization itself.\n\nCurrently supported viz families:\n - filter_response — filters with characterized response curves (e.g. Three Sisters, Ripples, Belgrad, A-124, Filter 8, QPAS, SVF 1U, Cinnamon, C4RBN, Ikarie)\n - oscillator_morph — multi-mode oscillators and excited resonators (e.g. Rings, Loquelic Iteritas, Plaits)\n\nA module is supported when every one of its modes has a behavior_model_id the renderer knows. If you\'re unsure whether a given module qualifies, just call this tool — the error names the gap.\n\nErrors:\n - "Module not found: <id>" if no module with that id exists.\n - "Module not yet supported by visualize_module: <id>" when one or more modes lack a renderer-known behavior_model_id, or when the module mixes incompatible viz families. Suggest get_module for the underlying spec.\n\nThe returned spec is a JSON object with: module_id, module_name, manufacturer, viz_type, params[], modes[], response_model_id, presets[]. Each mode has a behavior_model_id that the renderer uses to pick the curve set (e.g. crossover_lp_bp_hp vs formant_three_bp for filter_response).\n\n`response_model_id` (top-level) vs per-mode `behavior_model_id`: for multi-mode modules the top-level field is intentionally null — each mode carries its own behavior_model_id since the modes use different curve sets (e.g. Three Sisters\' crossover vs formant). Read the per-mode values from `modes[].behavior_model_id`. The top-level is populated only for single-curve modules where one model applies across the whole module. `null` at top-level + populated per-mode = "modes carry distinct models," not a bug.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'search_modules', 'name': 'search_modules', 'description': 'Search the corpus for Eurorack modules matching a combination of filters.\n\nFilters compose with AND. Omit any filter to leave that dimension unrestricted. The result is sorted by module name; pagination metadata in the response envelope lets you page through long result sets.\n\nArgs:\n - capability (string): capability id, e.g. \'envelope-generator\', \'clock-source\'. Run a search with NO capability filter to get the full capability taxonomy (ids + labels + counts) in _meta.taxonomy. Retired/variant slugs resolve via the capability_aliases layer (e.g. \'low-pass-gate\' → \'lowpass-gate\', \'quantiser\' → \'quantizer\'), so either form is accepted.\n - manufacturer (string): manufacturer id, e.g. \'make-noise\', \'mutable-instruments\'.\n - hp_min, hp_max (number): module width in HP. hp_max=10 finds modules ≤ 10 HP.\n - signal_type_in (string): the module accepts a jack of this signal type as input. One of audio, cv, gate, trigger, clock, mixed. signal_type_in=\'audio\' and =\'cv\' both also match jacks tagged \'mixed\' (the schema\'s value for jacks the source describes as accepting both audio and CV — e.g. Joranalogue Compare 2\'s signal inputs); the other values match literally.\n - signal_type_out (string): the module produces a jack of this signal type as output. Same \'mixed\'-superset semantics as signal_type_in.\n - text (string): free-text match against module id, name, slug, description, and the ids/labels/descriptions of capabilities the module has (case-insensitive substring). Matches hyphenated forms like "filter-8" against the slug/id even when the display name uses a space ("Filter 8"), and is whitespace-insensitive on id/slug/name so "3x MIA" finds the module named "3xMIA". Capability-label coverage means text="multiband" finds modules tagged multiband-filter without knowing the kebab-case id, and a curated alias layer extends that to common word-form variants ("multi-output" / "multi-band" / "band-split" → multiband-filter, "low-pass" → lowpass-filter, retired ids like "voltage-controlled-filter" → vcf). Truly novel wording still requires the _meta.taxonomy overview (run a no-capability search); if you expected a hit and got 0, call report_gap so the alias can be added.\n - voct_tracking_range_min (number): the module has a V/Oct input whose source-stated tracking range is at least this many octaves. Use for "filters that track 5+ octaves" / "oscillators with wide V/Oct range".\n - voct_tracking_quality (string): the module has a V/Oct input with this tracking quality, one of \'calibrated\', \'temperature-compensated\', \'approximate\', \'uncalibrated\'. \'temperature-compensated\' is the strongest claim.\n - voct_temperature_compensated (boolean): the module has a V/Oct input whose source explicitly states temperature compensation. Implies calibrated but separately flagged because some manuals call out only one.\n - audio_outputs_min (number): the module has at least this many output jacks with signal_type=\'audio\'. Use for "multi-output filters" (≥3 audio outs surfaces LP/BP/HP-tap VCFs like Three Sisters, QPAS, A-108, Polaris) or any multi-tap audio module. Combine with capability=\'vcf\' for the canonical multi-output-filter query.\n - limit (number): default 50, max 200.\n - offset (number): pagination offset.\n\nReturns:\n {\n "modules": [{ id, name, manufacturer, hp, capabilities: [string], description, production_status }],\n "total": number, // total matches (across all pages)\n "_meta": {\n "query": <args>,\n // Present whenever a \'capability\' filter matched >=1 module (NOT gated on\n // total=0 — it accompanies normal results). The category-coverage\n // denominator, so a "best X" recommendation can self-caveat instead of\n // reading as "best available":\n // On a no-capability search: the global capability taxonomy (id, label,\n // description, module_count) — discover the controlled vocabulary here\n // instead of a separate list_capabilities call.\n "taxonomy": [{ "id": "lowpass-gate", "label": "Low-pass gate", "module_count": 19 }],\n "coverage": {\n "capability": "stereo-mixer", // the capability you filtered on\n "category_total": 9, // modules in the corpus with this capability, IGNORING your other filters\n "corpus_total": 388, // all modules in the corpus\n "note": "...best of 9 in the corpus, not best available..." // ready-to-use recommendation caveat\n },\n // Present when the server\'s token-AND fallback rescued an otherwise-empty\n // phrase query (e.g. "pamela workout" → "Pamela\'s NEW Workout" via per-word\n // identifier match). Not an error; just signals that results came from the\n // relaxed pass rather than the literal phrase.\n "relaxed_to_tokens": true,\n // On total=0 (after the token-AND fallback has already been attempted), the\n // server adds these diagnostic hints so you can retry productively in one\n // turn instead of guessing variants. Each is independently optional:\n "would_match_without": ["capability", "text"], // filters that, if individually dropped, would yield ≥1 result — the named filter(s) cost you the match\n "closest_text_hits": [{ id, name, manufacturer }], // top 3 modules matching \'text\' alone (other filters dropped); inspect for a close hit you filtered out by accident\n "did_you_mean": [{ id, name, manufacturer }], // top 3 edit-distance neighbors of \'text\' when it matched nothing literally (a single-token typo like "multgrain" → multigrain); PRESENT means retry with the suggested id, ABSENT means the term is a genuine corpus gap (call report_gap) — the discriminator would_match_without can\'t give you\n "capability_suggestions": [{ id, label }], // top 3 valid capabilities matching the \'capability\' arg you passed (only set when the arg wasn\'t a known slug or alias) — use list_capabilities for the full taxonomy\n "manufacturer_suggestions": [{ id, name }], // top 3 maker slugs matching the \'manufacturer\' arg (only set when it wasn\'t a canonical slug) — the manufacturer arg is EXACT-match, so e.g. "addac" → "addac-system", "nonlinearcircuits" → "nlc"; retry with the suggested id\n "feedback_hint": "..." // fallback prompt to call report_gap when no other diagnostic applies\n }\n }\n\nExamples:\n - "What envelope generators under 8 HP exist?" → {capability: \'envelope-generator\', hp_max: 8}\n - "What ALM modules are in the corpus?" → {manufacturer: \'alm-busy-circuits\'}\n - "What clock sources are there?" → {signal_type_out: \'clock\'}\n - "Modules with \'workout\' in the name" → {text: \'workout\'}\n - "Filters that track V/Oct over 5 octaves" → {capability: \'vcf\', voct_tracking_range_min: 5}\n - "Temperature-compensated filter cores" → {voct_tracking_quality: \'temperature-compensated\'}\n - "Multi-output filters with LP/BP/HP taps" → {capability: \'vcf\', audio_outputs_min: 3}\n\nErrors:\n - Returns an empty modules array (and total=0) if nothing matches. Not an error — inspect _meta.would_match_without / closest_text_hits / capability_suggestions / manufacturer_suggestions to decide whether to broaden the query or call report_gap.\n - Invalid filter values pass through to the WHERE clause; if no module satisfies them you get total=0.\n\nAfter picking a hit, call get_module with the id for full details.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'resolve_modules', 'name': 'resolve_modules', 'description': 'Bulk fuzzy name → id resolver. Pass a list of module NAMES (the way a user writes their rack — "Maths", "Plaits", "Pamela\'s New Workout", "Morph 4") and get back, per name, the best-matching corpus module id plus recovery affordances. This is the "import my rack" tool: resolving a 60-module inventory is ONE call here, not 60 search_modules calls.\n\nUse this the moment a user gives you a list of module names to map onto the corpus — before get_modules / draw_patch_diagram / reachable_techniques / rack_redundancy, all of which want ids. Feed the resolved best_match.id into those.\n\nFor each input name it runs the same resolution search_modules uses (exact id, unique-slug recovery, module_aliases, separator-fold, token-AND, and edit-distance + multi-token fuzzy "did you mean"), then returns:\n - best_match: the single resolved module ({ id, name, manufacturer, hp, capabilities, production_status }), or null when nothing resolved confidently.\n - score: confidence 0..1 (1.0 exact id, ~0.97 unique slug / alias, ~0.9 literal name hit, ~0.72 token-AND relaxed, 0 when best_match is null).\n - match_kind: "id" | "slug" | "exact" | "relaxed" | "ambiguous" | "fuzzy" | "none".\n - did_you_mean: up to 5 fuzzy neighbours to confirm — populated when best_match is null (a near-miss like "morf 4" → joranalogue/morph-4, or "tiny time machin" → oam/tiny-time-machine).\n - ambiguous: up to 5 candidates when several modules tied for the match and the server declined to pick (e.g. a slug shared across manufacturers). Present them to the user; do NOT silently take the first.\n\nThe server AUTO-RESOLVES a confident single match but never guesses through a genuine tie — that\'s the ambiguous bucket. A name with best_match=null AND empty did_you_mean/ambiguous is a real corpus gap: consider report_gap(kind="missing_module").\n\nArgs:\n - names (string[], required): module names or ids, up to 100. Order and duplicates are preserved in the response.\n\nReturns:\n {\n "resolutions": [\n {\n "input": "Pamela\'s New Workout",\n "best_match": { "id": "alm-busy-circuits/pamelas-new-workout", "name": "Pamela\'s NEW Workout", "manufacturer": "ALM Busy Circuits", "hp": 8, "capabilities": [...], "production_status": "current" },\n "score": 0.9,\n "match_kind": "exact",\n "did_you_mean": [],\n "ambiguous": []\n },\n ...\n ],\n "_meta": { "requested": 3, "resolved": 2, "feedback_hint"?: "..." }\n }\n\nErrors:\n - Throws only on a malformed call (missing/empty names array, or > 100 names). Unresolved individual names are NOT errors — they come back with best_match=null and the recovery fields.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'search_manual', 'name': 'search_manual', 'description': 'Full-text search across parsed module manuals, product pages, and firmware release notes.\n\nUse this only when the question is about content that lives in continuous prose rather than in typed fields:\n - Procedural: calibration sequences, button combos, factory-reset steps, save/load procedures.\n - Diagnostic: LED color meanings, error indicators, troubleshooting trees.\n - Firmware specifics beyond the short notes on firmware_versions (which only carry the headline change).\n - Panel walkthroughs and prose explanations that aren\'t captured as parameters/jacks/zones.\n\nDo NOT call this for content already in get_module: a parameter\'s behavior, a jack\'s signal type or polarity, a mode\'s name or description, capability tags, HP, or power draw. Those are typed and authoritative there.\n\nDo NOT call this to summarize a module — that\'s get_module\'s job. search_manual returns excerpts, not summaries.\n\nReturned chunks are source prose, not typed facts. Read them to ground your answer, then paraphrase and point the user to the source to verify (cite-and-point, not reproduce — SKILL.md §8). `text` is capped (~800 chars); the full passage is at audit_url.\n\nArgs:\n - query (string, required): search terms. Plain words are AND\'d by default ("calibration LED" matches chunks mentioning both). If the AND match returns 0 rows, the server retries with OR (any-token match) and sets _meta.relaxed_to_or=true on the response — so a long natural-language query like "cascade mode time inner outer delay" still surfaces something useful instead of zeroing out. Best practice is still 2–4 distinctive keywords; the OR fallback is a safety net, not a substitute. Query is tokenized to alphanumeric runs; FTS5 punctuation is stripped.\n - module_id (string, optional): "<manufacturer>/<module-slug>" — restrict to one module. Strongly recommended when the user has named a module.\n - source_id (integer, optional): restrict to one source. Use when you already have a source_id from get_source / list_references and want to dig into that specific document.\n - source_type (string, optional): one of "manual", "product_page", "firmware_notes". Defaults to all.\n - limit (integer, optional): default 5, max 20. Smaller is usually better — top-3 hits cover most queries.\n\nReturns:\n {\n "query": string,\n "matches": [{\n "chunk_id": number,\n "source_id": number,\n "source_type": string,\n "source_title": string | null,\n "module_id": string | null,\n "heading_path": string, // "Calibration > Tuning Procedure"\n "snippet": string, // BM25-highlighted excerpt with [matches] in brackets\n "text": string, // chunk text, capped ~800 chars; paraphrase, don\'t paste\n "truncated": boolean, // true if text was trimmed; full passage at audit_url\n "audit_url": string, // human-readable audit page for the source\n "rank": number // BM25 score (more negative = better match)\n }],\n "total": number, // total matches across the corpus (capped at 200)\n "_meta": {\n "kind": "manual_excerpt",\n "query": <args>,\n "relaxed_to_or": true // only present when AND returned 0 and OR retry fired\n }\n }\n\nExamples:\n - "How do I calibrate Plaits\' V/Oct?" → { query: "calibration V/Oct", module_id: "mutable-instruments/plaits" }\n - "What does the red LED on Marbles mean?" → { query: "red LED", module_id: "mutable-instruments/marbles" }\n - "How do I reset Pamela\'s New Workout to defaults?" → { query: "factory reset defaults", module_id: "alm-busy-circuits/pamelas-new-workout" }\n - "What changed in Plaits firmware 1.2?" → { query: "1.2", module_id: "mutable-instruments/plaits", source_type: "firmware_notes" }\n\nErrors:\n - Returns matches=[] with total=0 if nothing matches. Not an error.\n - Errors only on malformed input (missing query, invalid limit, unknown source_type).', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'get_manual_chunk', 'name': 'get_manual_chunk', 'description': 'Read one manual chunk by id to ground your answer in the manufacturer\'s documentation — the drill-in partner to get_module\'s manual_outline. Use it when a claim (voltage range, behavior, calibration step) benefits from the source\'s own account.\n\n**Read the chunk, then answer in your own words and point the user to the source (audit_url) to verify.** Paraphrase the substance; do not paste the chunk back as your reply. Quote verbatim only when the exact wording is load-bearing for a specific contested detail, and then only a short phrase. Citation style is SKILL.md §8 (cite-and-point, don\'t reproduce).\n\n`text` is **capped** (~800 chars): enough to ground an answer, not to reproduce the manual. If truncated:true, a " […]" marks the cut and the full passage is at audit_url — send the user there for the rest rather than stitching chunks together to rebuild it.\n\nWhen to call:\n - You have a chunk_id from get_module\'s manual_outline that looks relevant to the question.\n - You\'re writing an answer with specific technical claims and want the source\'s account to ground (and, if needed, briefly cite) it.\n - The user asked a prose-shaped question ("what does the manual say about X?") — read the chunk, then explain it in your own words with a link to verify.\n\nPrefer get_manual_chunk over search_manual when:\n - You already have the chunk_id from get_module\'s manual_outline.\n - You want exactly one chunk, not a ranked list.\n\nReturns:\n {\n "chunk_id": number,\n "source_id": number,\n "source_type": string, // "manual" | "product_page" | "firmware_notes"\n "source_title": string | null,\n "heading_path": string | null, // e.g. "Calibration > Tuning Procedure"\n "text": string, // chunk text, capped ~800 chars; paraphrase, don\'t paste\n "truncated": boolean, // true if text was trimmed; full passage at audit_url\n "audit_url": string // human-readable audit page for the source\n }\n\nErrors:\n - "Manual chunk not found: <id>" if the chunk_id doesn\'t exist.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}, {'id': 'find_compatible_with', 'name': 'find_compatible_with', 'description': 'Return modules that have a typed compatibility relationship with the given module. Both edge directions are returned and tagged via the per-match `direction` field — so a single call answers both "what is X a R for?" and "what is a R for X?".\n\n`relationship` is OPTIONAL. Omit it to get EVERY edge touching the module across all relationship kinds — the bare "what pairs with / relates to X?" question — with each match self-describing via its own `relationship`. Pass a relationship to restrict to that one kind. Prefer the relationship-less call when you don\'t already know which kind exists; reach for the typed form only when the question names a specific role ("what clocks X?").\n\nUse this for two question shapes:\n 1. Patch-time compatibility — "what could I use as a clock source for X?" (returns matches with direction=\'inbound\'), or "what does X clock?" (direction=\'outbound\').\n 2. Catalog comparison — "what\'s an alternative to X?" (symmetric), "what does X replace?" (outbound) / "what replaces X?" (inbound), "is there an expander for X?" (inbound).\n\nThe vocabulary describes the edge as stored (from = role-bearer, to = target):\n\n Patch-time:\n - clock-source-for — A clocks B\n - cv-source-for — A produces CV that B consumes\n - modulator-for — A is a modulator suitable for B (LFO, S&H, random)\n - audio-source-for — A is an audio source for B (typically a VCO into a VCF)\n - quantizer-for — A quantizes for B\n - trigger-source-for — A produces triggers that B consumes\n - envelope-target-for — A is something B\'s envelope output is designed to drive\n Catalog:\n - replaces — A is the newer successor to B (Morphagene replaces Phonogene)\n - alternative-to — symmetric: A and B occupy similar design space with different character\n - expander-for — A is an expander module for the host module B\n\nDirection tag on each match:\n - outbound: queried module is the FROM side (role-bearer). Match is what the queried module does as R.\n - inbound: queried module is the TO side. Match is the R-for the queried module.\n - symmetric: only for alternative-to.\n\nArgs:\n - module_id (string, required): "<manufacturer-slug>/<module-slug>"\n - relationship (string, optional): one of the values above. Omit for all edges.\n - limit (number): default 50, max 200\n\nReturns:\n {\n "module": { id, name },\n "relationship": <relationship> | null, // null when none was passed (all-edges query)\n "matches": [{ id, name, manufacturer, notes, source_id, direction, relationship }]\n }\n\nIf the module is unknown, returns an error. If no relationships have been recorded in either direction, returns matches=[]. The `notes` field describes the edge in the canonical A→B direction; combined with `direction` the caller can read it correctly either way.', 'tags': [], 'examples': None, 'input_modes': None, 'output_modes': None}; uptime_30d 1.0%; p95 936.6ms; conformance: pass
How to connect
https://eurorackref.com/mcp
curl -X POST https://eurorackref.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'