diff --git a/docs/codex-claude-mcp-setup.md b/docs/codex-claude-mcp-setup.md new file mode 100644 index 00000000..bb7b566a --- /dev/null +++ b/docs/codex-claude-mcp-setup.md @@ -0,0 +1,117 @@ +# Codex and Claude MCP Setup + +## Goal + +Use MiroFish as a shared prediction engine through one MCP server so: + +- Codex can build and operate simulation flows +- Claude can reason over the same simulation and report state +- both tools stay aligned on one backend + +## Recommended Shape + +- Run the MiroFish backend locally +- Run one MCP server against that backend +- Point Codex and Claude at the same MCP server + +```text +Codex ----\ + >---- MiroFish MCP ---- MiroFish backend +Claude ---/ +``` + +## Start Order + +From [`/Users/al/Documents/CODEX/MiroFish`](/Users/al/Documents/CODEX/MiroFish): + +```bash +npm run backend +``` + +In another terminal: + +```bash +npm run mcp:stdio +``` + +If you prefer HTTP transport instead of stdio: + +```bash +MIROFISH_BASE_URL=http://127.0.0.1:5001 npm run mcp:http +``` + +## Codex Connection + +Register the MCP server in Codex using the command below as the server command: + +```bash +cd /Users/al/Documents/CODEX/MiroFish/backend && uv run python ../integrations/mcp/mirofish_mcp_server.py --transport stdio +``` + +Recommended environment: + +```text +MIROFISH_BASE_URL=http://127.0.0.1:5001 +MIROFISH_HTTP_TIMEOUT=60 +``` + +Use stdio for local Codex work unless you specifically need HTTP transport for shared remote access. + +## Claude Connection + +Register the same MCP server command in Claude's MCP settings: + +```bash +cd /Users/al/Documents/CODEX/MiroFish/backend && uv run python ../integrations/mcp/mirofish_mcp_server.py --transport stdio +``` + +Use the same environment values: + +```text +MIROFISH_BASE_URL=http://127.0.0.1:5001 +MIROFISH_HTTP_TIMEOUT=60 +``` + +This keeps Codex and Claude on one shared tool surface and avoids separate wrapper logic. + +## MCP Tools You Can Rely On + +- `mirofish_health` +- `mirofish_list_projects` +- `mirofish_get_project` +- `mirofish_build_graph` +- `mirofish_get_graph_task` +- `mirofish_create_simulation` +- `mirofish_prepare_simulation` +- `mirofish_prepare_status` +- `mirofish_start_simulation` +- `mirofish_stop_simulation` +- `mirofish_simulation_status` +- `mirofish_simulation_timeline` +- `mirofish_interview_agents` +- `mirofish_generate_report` +- `mirofish_report_status` +- `mirofish_get_report` +- `mirofish_get_report_by_simulation` +- `mirofish_chat_report` + +## Recommended Operator Flow + +1. Confirm backend health with `mirofish_health`. +2. Choose an existing project with `mirofish_list_projects`. +3. Build or rebuild the graph with `mirofish_build_graph`. +4. Poll graph completion with `mirofish_get_graph_task`. +5. Create a simulation with `mirofish_create_simulation`. +6. Prepare the simulation with `mirofish_prepare_simulation`. +7. Poll readiness with `mirofish_prepare_status`. +8. Start the run with `mirofish_start_simulation`. +9. Poll runtime with `mirofish_simulation_status`. +10. Generate the report with `mirofish_generate_report`. +11. Fetch the final output with `mirofish_get_report_by_simulation`. +12. Ask follow-up questions with `mirofish_chat_report`. + +## Notes + +- Real simulation runs still require valid `LLM_API_KEY` and `ZEP_API_KEY`. +- The MCP server is a control layer, not a replacement for the MiroFish backend. +- n8n should call the backend directly for scheduled runs unless you explicitly want agent-mediated orchestration. diff --git a/docs/integration-stack.md b/docs/integration-stack.md new file mode 100644 index 00000000..6afa4ebf --- /dev/null +++ b/docs/integration-stack.md @@ -0,0 +1,127 @@ +# MiroFish Integration Stack + +## Goal + +Use **MiroFish** as the prediction engine and simulation brain, with: + +- **Codex** as the builder and operator +- **Claude** as the reasoning and synthesis layer +- **n8n** as the automation layer +- **MCP** as the shared tool access layer + +## Recommended Architecture + +```text +Codex / Claude + | + v + MCP tools + | + v +MiroFish backend API + | + +--> graph build + +--> simulation lifecycle + +--> interviews with agents + +--> report generation and chat + | + v + MiroFish frontend + +n8n + | + +--> calls MiroFish backend directly over HTTP + +--> or invokes the same lifecycle through the MCP server +``` + +## Roles + +### Codex + +- builds workflow glue, prompts, and wrappers +- drives MiroFish through MCP tools +- prepares projects, simulations, and reports + +### Claude + +- consumes simulation outputs and reports +- performs deeper reasoning on what the simulation means +- uses the same MCP tools so it sees the same state as Codex + +### n8n + +- schedules or triggers prediction runs +- polls preparation/report task status +- routes outputs to Slack, email, dashboards, or databases + +### MCP + +- standardizes access to MiroFish +- gives Codex and Claude a stable tool layer +- avoids coupling every agent directly to raw REST details + +## Added in This Setup + +- MCP server: [`integrations/mcp/mirofish_mcp_server.py`](/Users/al/Documents/CODEX/MiroFish/integrations/mcp/mirofish_mcp_server.py) +- start scripts: + - [`scripts/start_mirofish_mcp_stdio.sh`](/Users/al/Documents/CODEX/MiroFish/scripts/start_mirofish_mcp_stdio.sh) + - [`scripts/start_mirofish_mcp_http.sh`](/Users/al/Documents/CODEX/MiroFish/scripts/start_mirofish_mcp_http.sh) +- Codex and Claude setup guide: + - [`docs/codex-claude-mcp-setup.md`](/Users/al/Documents/CODEX/MiroFish/docs/codex-claude-mcp-setup.md) +- n8n workflow template: + - [`integrations/n8n/mirofish_prediction_pipeline.json`](/Users/al/Documents/CODEX/MiroFish/integrations/n8n/mirofish_prediction_pipeline.json) +- npm scripts: + - `npm run mcp:stdio` + - `npm run mcp:http` + +## MCP Tools Exposed + +- health +- project list +- project detail +- graph build +- graph task status +- simulation create +- simulation prepare +- simulation prepare status +- simulation start +- simulation stop +- simulation run status +- simulation timeline +- batch interviews +- report generate +- report generate status +- report fetch +- report fetch by simulation +- report chat + +## n8n Automation Pattern + +Use HTTP Request nodes against the backend: + +1. `POST /api/graph/build` +2. poll `GET /api/graph/task/{task_id}` +3. `POST /api/simulation/create` +4. `POST /api/simulation/prepare` +5. poll `POST /api/simulation/prepare/status` +6. `POST /api/simulation/start` +7. poll `GET /api/simulation/{simulation_id}/run-status` +8. `POST /api/report/generate` +9. poll `POST /api/report/generate/status` +10. `GET /api/report/by-simulation/{simulation_id}` + +An importable starter workflow is included at +[`integrations/n8n/mirofish_prediction_pipeline.json`](/Users/al/Documents/CODEX/MiroFish/integrations/n8n/mirofish_prediction_pipeline.json). + +## Suggested Operating Mode + +- Let Codex build and evolve the simulation workflow. +- Let Claude reason over the generated report and interview outputs. +- Let n8n handle timed or event-driven execution. +- Treat MiroFish as the core prediction substrate, not the orchestration system. + +## Notes + +- Real simulation quality depends on valid `LLM_API_KEY` and `ZEP_API_KEY`. +- Local boot verification can use placeholder values, but real runs cannot. +- Python 3.12 is required for this repo on this Mac. diff --git a/integrations/mcp/mirofish_mcp_server.py b/integrations/mcp/mirofish_mcp_server.py new file mode 100644 index 00000000..738b9058 --- /dev/null +++ b/integrations/mcp/mirofish_mcp_server.py @@ -0,0 +1,213 @@ +from __future__ import annotations + +import argparse +import os +from typing import Any + +import httpx +from mcp.server.fastmcp import FastMCP + + +DEFAULT_BASE_URL = os.environ.get("MIROFISH_BASE_URL", "http://127.0.0.1:5001") +DEFAULT_TIMEOUT = float(os.environ.get("MIROFISH_HTTP_TIMEOUT", "60")) + +mcp = FastMCP( + name="MiroFish MCP", + instructions=( + "Use these tools to drive MiroFish as a prediction engine and simulation brain. " + "Create a project graph first, then create a simulation, prepare it, start it, " + "and generate or fetch reports." + ), +) + + +def _base_url() -> str: + return os.environ.get("MIROFISH_BASE_URL", DEFAULT_BASE_URL).rstrip("/") + + +def _request(method: str, path: str, *, params: dict[str, Any] | None = None, json_body: dict[str, Any] | None = None) -> dict[str, Any]: + url = f"{_base_url()}{path}" + with httpx.Client(timeout=DEFAULT_TIMEOUT) as client: + response = client.request(method, url, params=params, json=json_body) + response.raise_for_status() + content_type = response.headers.get("content-type", "") + if "application/json" in content_type: + return response.json() + return {"success": True, "status_code": response.status_code, "text": response.text} + + +@mcp.tool() +def mirofish_health() -> dict[str, Any]: + return _request("GET", "/health") + + +@mcp.tool() +def mirofish_list_projects() -> dict[str, Any]: + return _request("GET", "/api/graph/project/list") + + +@mcp.tool() +def mirofish_get_project(project_id: str) -> dict[str, Any]: + return _request("GET", f"/api/graph/project/{project_id}") + + +@mcp.tool() +def mirofish_build_graph(payload: dict[str, Any]) -> dict[str, Any]: + return _request("POST", "/api/graph/build", json_body=payload) + + +@mcp.tool() +def mirofish_get_graph_task(task_id: str) -> dict[str, Any]: + return _request("GET", f"/api/graph/task/{task_id}") + + +@mcp.tool() +def mirofish_create_simulation( + project_id: str, + graph_id: str | None = None, + enable_twitter: bool = True, + enable_reddit: bool = True, +) -> dict[str, Any]: + payload = { + "project_id": project_id, + "enable_twitter": enable_twitter, + "enable_reddit": enable_reddit, + } + if graph_id: + payload["graph_id"] = graph_id + return _request("POST", "/api/simulation/create", json_body=payload) + + +@mcp.tool() +def mirofish_prepare_simulation( + simulation_id: str, + entity_types: list[str] | None = None, + use_llm_for_profiles: bool = True, + parallel_profile_count: int = 5, + force_regenerate: bool = False, +) -> dict[str, Any]: + payload: dict[str, Any] = { + "simulation_id": simulation_id, + "use_llm_for_profiles": use_llm_for_profiles, + "parallel_profile_count": parallel_profile_count, + "force_regenerate": force_regenerate, + } + if entity_types: + payload["entity_types"] = entity_types + return _request("POST", "/api/simulation/prepare", json_body=payload) + + +@mcp.tool() +def mirofish_prepare_status(task_id: str) -> dict[str, Any]: + return _request("POST", "/api/simulation/prepare/status", json_body={"task_id": task_id}) + + +@mcp.tool() +def mirofish_start_simulation( + simulation_id: str, + platform: str = "parallel", + max_rounds: int | None = None, + enable_graph_memory_update: bool = False, + force: bool = False, +) -> dict[str, Any]: + payload: dict[str, Any] = { + "simulation_id": simulation_id, + "platform": platform, + "enable_graph_memory_update": enable_graph_memory_update, + "force": force, + } + if max_rounds is not None: + payload["max_rounds"] = max_rounds + return _request("POST", "/api/simulation/start", json_body=payload) + + +@mcp.tool() +def mirofish_stop_simulation(simulation_id: str) -> dict[str, Any]: + return _request("POST", "/api/simulation/stop", json_body={"simulation_id": simulation_id}) + + +@mcp.tool() +def mirofish_simulation_status(simulation_id: str, detailed: bool = False) -> dict[str, Any]: + suffix = "/run-status/detail" if detailed else "/run-status" + return _request("GET", f"/api/simulation/{simulation_id}{suffix}") + + +@mcp.tool() +def mirofish_simulation_timeline(simulation_id: str) -> dict[str, Any]: + return _request("GET", f"/api/simulation/{simulation_id}/timeline") + + +@mcp.tool() +def mirofish_interview_agents( + simulation_id: str, + interviews: list[dict[str, Any]], + platform: str = "parallel", +) -> dict[str, Any]: + return _request( + "POST", + "/api/simulation/interview/batch", + json_body={ + "simulation_id": simulation_id, + "platform": platform, + "interviews": interviews, + }, + ) + + +@mcp.tool() +def mirofish_generate_report(simulation_id: str, force_regenerate: bool = False) -> dict[str, Any]: + return _request( + "POST", + "/api/report/generate", + json_body={"simulation_id": simulation_id, "force_regenerate": force_regenerate}, + ) + + +@mcp.tool() +def mirofish_report_status(task_id: str) -> dict[str, Any]: + return _request("POST", "/api/report/generate/status", json_body={"task_id": task_id}) + + +@mcp.tool() +def mirofish_get_report(report_id: str) -> dict[str, Any]: + return _request("GET", f"/api/report/{report_id}") + + +@mcp.tool() +def mirofish_get_report_by_simulation(simulation_id: str) -> dict[str, Any]: + return _request("GET", f"/api/report/by-simulation/{simulation_id}") + + +@mcp.tool() +def mirofish_chat_report( + simulation_id: str, + message: str, + chat_history: list[dict[str, str]] | None = None, +) -> dict[str, Any]: + payload: dict[str, Any] = { + "simulation_id": simulation_id, + "message": message, + } + if chat_history: + payload["chat_history"] = chat_history + return _request("POST", "/api/report/chat", json_body=payload) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Expose MiroFish backend APIs over MCP.") + parser.add_argument("--transport", choices=["stdio", "http"], default="stdio") + parser.add_argument("--host", default=os.environ.get("MIROFISH_MCP_HOST", "127.0.0.1")) + parser.add_argument("--port", type=int, default=int(os.environ.get("MIROFISH_MCP_PORT", "8000"))) + args = parser.parse_args() + + if args.transport == "http": + mcp.settings.host = args.host + mcp.settings.port = args.port + mcp.run(transport="streamable-http") + return + + mcp.run() + + +if __name__ == "__main__": + main() diff --git a/integrations/n8n/mirofish_prediction_pipeline.json b/integrations/n8n/mirofish_prediction_pipeline.json new file mode 100644 index 00000000..b4ca5347 --- /dev/null +++ b/integrations/n8n/mirofish_prediction_pipeline.json @@ -0,0 +1,650 @@ +{ + "name": "MiroFish Prediction Pipeline", + "nodes": [ + { + "parameters": {}, + "id": "manual-trigger", + "name": "Manual Trigger", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + -1360, + 160 + ] + }, + { + "parameters": { + "assignments": { + "assignments": [ + { + "id": "mirofish-base-url", + "name": "mirofishBaseUrl", + "value": "http://127.0.0.1:5001", + "type": "string" + }, + { + "id": "project-id", + "name": "projectId", + "value": "proj_replace_me", + "type": "string" + }, + { + "id": "graph-name", + "name": "graphName", + "value": "Prediction Brain Graph", + "type": "string" + }, + { + "id": "chunk-size", + "name": "chunkSize", + "value": 500, + "type": "number" + }, + { + "id": "chunk-overlap", + "name": "chunkOverlap", + "value": 50, + "type": "number" + }, + { + "id": "enable-twitter", + "name": "enableTwitter", + "value": true, + "type": "boolean" + }, + { + "id": "enable-reddit", + "name": "enableReddit", + "value": true, + "type": "boolean" + }, + { + "id": "use-llm-profiles", + "name": "useLlmForProfiles", + "value": true, + "type": "boolean" + }, + { + "id": "parallel-profile-count", + "name": "parallelProfileCount", + "value": 5, + "type": "number" + }, + { + "id": "max-rounds", + "name": "maxRounds", + "value": 24, + "type": "number" + } + ] + }, + "options": {} + }, + "id": "set-inputs", + "name": "Set Inputs", + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + -1140, + 160 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $json.mirofishBaseUrl + '/api/graph/build' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { project_id: $json.projectId, graph_name: $json.graphName, chunk_size: $json.chunkSize, chunk_overlap: $json.chunkOverlap } }}", + "options": {} + }, + "id": "build-graph", + "name": "Build Graph", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -920, + 160 + ] + }, + { + "parameters": { + "method": "GET", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/graph/task/' + $json.data.task_id }}", + "options": {} + }, + "id": "poll-graph-task", + "name": "Poll Graph Task", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -700, + 160 + ] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.data.status }}", + "operation": "equal", + "value2": "completed" + } + ] + } + }, + "id": "graph-complete", + "name": "Graph Complete?", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + -480, + 160 + ] + }, + { + "parameters": { + "amount": 20, + "unit": "seconds" + }, + "id": "wait-graph", + "name": "Wait Graph", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [ + -480, + 320 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/simulation/create' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { project_id: $('Set Inputs').item.json.projectId, enable_twitter: $('Set Inputs').item.json.enableTwitter, enable_reddit: $('Set Inputs').item.json.enableReddit } }}", + "options": {} + }, + "id": "create-simulation", + "name": "Create Simulation", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -260, + 80 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/simulation/prepare' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { simulation_id: $json.data.simulation_id, use_llm_for_profiles: $('Set Inputs').item.json.useLlmForProfiles, parallel_profile_count: $('Set Inputs').item.json.parallelProfileCount } }}", + "options": {} + }, + "id": "prepare-simulation", + "name": "Prepare Simulation", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + -40, + 80 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/simulation/prepare/status' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { task_id: $json.data.task_id } }}", + "options": {} + }, + "id": "poll-prepare-status", + "name": "Poll Prepare Status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 180, + 80 + ] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.data.status }}", + "operation": "equal", + "value2": "ready" + } + ] + } + }, + "id": "prepare-ready", + "name": "Prepare Ready?", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 400, + 80 + ] + }, + { + "parameters": { + "amount": 30, + "unit": "seconds" + }, + "id": "wait-prepare", + "name": "Wait Prepare", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [ + 400, + 240 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/simulation/start' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { simulation_id: $('Create Simulation').item.json.data.simulation_id, platform: 'parallel', max_rounds: $('Set Inputs').item.json.maxRounds, enable_graph_memory_update: false, force: false } }}", + "options": {} + }, + "id": "start-simulation", + "name": "Start Simulation", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 620, + 0 + ] + }, + { + "parameters": { + "method": "GET", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/simulation/' + $('Create Simulation').item.json.data.simulation_id + '/run-status' }}", + "options": {} + }, + "id": "poll-run-status", + "name": "Poll Run Status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 840, + 0 + ] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.data.status }}", + "operation": "equal", + "value2": "completed" + } + ] + } + }, + "id": "simulation-complete", + "name": "Simulation Complete?", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 1060, + 0 + ] + }, + { + "parameters": { + "amount": 60, + "unit": "seconds" + }, + "id": "wait-simulation", + "name": "Wait Simulation", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [ + 1060, + 160 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/report/generate' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { simulation_id: $('Create Simulation').item.json.data.simulation_id, force_regenerate: false } }}", + "options": {} + }, + "id": "generate-report", + "name": "Generate Report", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 1280, + -80 + ] + }, + { + "parameters": { + "method": "POST", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/report/generate/status' }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { task_id: $json.data.task_id } }}", + "options": {} + }, + "id": "poll-report-status", + "name": "Poll Report Status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 1500, + -80 + ] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.data.status }}", + "operation": "equal", + "value2": "completed" + } + ] + } + }, + "id": "report-complete", + "name": "Report Complete?", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 1720, + -80 + ] + }, + { + "parameters": { + "amount": 30, + "unit": "seconds" + }, + "id": "wait-report", + "name": "Wait Report", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [ + 1720, + 80 + ] + }, + { + "parameters": { + "method": "GET", + "url": "={{ $('Set Inputs').item.json.mirofishBaseUrl + '/api/report/by-simulation/' + $('Create Simulation').item.json.data.simulation_id }}", + "options": {} + }, + "id": "fetch-report", + "name": "Fetch Report", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 1940, + -160 + ] + } + ], + "connections": { + "Manual Trigger": { + "main": [ + [ + { + "node": "Set Inputs", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set Inputs": { + "main": [ + [ + { + "node": "Build Graph", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build Graph": { + "main": [ + [ + { + "node": "Poll Graph Task", + "type": "main", + "index": 0 + } + ] + ] + }, + "Poll Graph Task": { + "main": [ + [ + { + "node": "Graph Complete?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Graph Complete?": { + "main": [ + [ + { + "node": "Create Simulation", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Wait Graph", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait Graph": { + "main": [ + [ + { + "node": "Poll Graph Task", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Simulation": { + "main": [ + [ + { + "node": "Prepare Simulation", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Simulation": { + "main": [ + [ + { + "node": "Poll Prepare Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Poll Prepare Status": { + "main": [ + [ + { + "node": "Prepare Ready?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Prepare Ready?": { + "main": [ + [ + { + "node": "Start Simulation", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Wait Prepare", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait Prepare": { + "main": [ + [ + { + "node": "Poll Prepare Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Start Simulation": { + "main": [ + [ + { + "node": "Poll Run Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Poll Run Status": { + "main": [ + [ + { + "node": "Simulation Complete?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Simulation Complete?": { + "main": [ + [ + { + "node": "Generate Report", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Wait Simulation", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait Simulation": { + "main": [ + [ + { + "node": "Poll Run Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Generate Report": { + "main": [ + [ + { + "node": "Poll Report Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Poll Report Status": { + "main": [ + [ + { + "node": "Report Complete?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Report Complete?": { + "main": [ + [ + { + "node": "Fetch Report", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Wait Report", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait Report": { + "main": [ + [ + { + "node": "Poll Report Status", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": {}, + "pinData": {}, + "meta": { + "templateCredsSetupCompleted": false + }, + "versionId": "1" +} diff --git a/package.json b/package.json index 3baf424a..1a165054 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "setup:all": "npm run setup && npm run setup:backend", "dev": "concurrently --kill-others -n \"backend,frontend\" -c \"green,cyan\" \"npm run backend\" \"npm run frontend\"", "backend": "cd backend && uv run python run.py", + "mcp:stdio": "cd backend && uv run python ../integrations/mcp/mirofish_mcp_server.py --transport stdio", + "mcp:http": "cd backend && uv run python ../integrations/mcp/mirofish_mcp_server.py --transport http", "frontend": "cd frontend && npm run dev", "build": "cd frontend && npm run build" }, diff --git a/scripts/start_mirofish_mcp_http.sh b/scripts/start_mirofish_mcp_http.sh new file mode 100755 index 00000000..0ad595b3 --- /dev/null +++ b/scripts/start_mirofish_mcp_http.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +cd /Users/al/Documents/CODEX/MiroFish/backend +uv run python ../integrations/mcp/mirofish_mcp_server.py --transport http diff --git a/scripts/start_mirofish_mcp_stdio.sh b/scripts/start_mirofish_mcp_stdio.sh new file mode 100755 index 00000000..e39d47e1 --- /dev/null +++ b/scripts/start_mirofish_mcp_stdio.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +cd /Users/al/Documents/CODEX/MiroFish/backend +uv run python ../integrations/mcp/mirofish_mcp_server.py --transport stdio