Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: MCP_TRANSPORT=sse PORT=$PORT python -m neuro_ghost.mcp_server
28 changes: 25 additions & 3 deletions neuro_ghost/ingest_linkml.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,38 @@ def parse_linkml(path: Path) -> dict[str, Any]:
slots: dict[str, dict] = {}

for cls_name in sv.all_classes():
cls_def = sv.get_class(cls_name)
induced_slots = sv.class_induced_slots(cls_name)
cls_def = sv.get_class(cls_name)

try:
induced_slots = sv.class_induced_slots(cls_name)
except (ValueError, KeyError):
# is_a points to a class outside this schema (e.g. NWB's "Container")
# Fall back to directly-declared slots only.
induced_slots = list(cls_def.attributes.values())
for sname in (cls_def.slots or []):
try:
s = sv.get_slot(sname)
if s:
induced_slots.append(s)
except Exception:
pass

class_uri = cls_def.class_uri or ""
resolved_iri = resolve_prefix(class_uri, prefixes) if class_uri else ""

# Strip is_a if the parent class isn't in this schema — build_registry_entities
# already handles None gracefully; leaving a dangling name would be misleading.
is_a = cls_def.is_a
if is_a:
try:
sv.get_class(is_a)
except (ValueError, KeyError):
is_a = None

classes[cls_name] = {
"iri": resolved_iri,
"definition": cls_def.description or "",
"is_a": cls_def.is_a,
"is_a": is_a,
"is_abstract": bool(cls_def.abstract),
"slots": [slot.name for slot in induced_slots],
}
Expand Down
14 changes: 12 additions & 2 deletions neuro_ghost/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,18 @@ def ingest_schema(
# ---------------------------------------------------------------------------

def main() -> None:
"""Entry point for the `neuroghost-mcp` console script."""
mcp.run()
"""Entry point for the `neuroghost-mcp` console script.

Transport is selected by the MCP_TRANSPORT env var:
stdio (default) — for Claude Desktop / Cursor / local use
sse — HTTP+SSE for Smithery and other hosted platforms
"""
transport = os.environ.get("MCP_TRANSPORT", "stdio")
if transport == "sse":
port = int(os.environ.get("PORT", "8000"))
mcp.run(transport="sse", host="0.0.0.0", port=port)
else:
mcp.run()


if __name__ == "__main__":
Expand Down
6 changes: 6 additions & 0 deletions railway.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[build]
builder = "nixpacks"

[deploy]
startCommand = "MCP_TRANSPORT=sse python -m neuro_ghost.mcp_server"
restartPolicyType = "on_failure"
Loading