Merge f2a38c9024 into b5b53acc57
This commit is contained in:
commit
665271419e
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
"setup:all": "npm run setup && npm run setup:backend",
|
"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\"",
|
"dev": "concurrently --kill-others -n \"backend,frontend\" -c \"green,cyan\" \"npm run backend\" \"npm run frontend\"",
|
||||||
"backend": "cd backend && uv run python run.py",
|
"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",
|
"frontend": "cd frontend && npm run dev",
|
||||||
"build": "cd frontend && npm run build"
|
"build": "cd frontend && npm run build"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue