Add MiroFish MCP and n8n integration stack
This commit is contained in:
parent
fa0f6519b1
commit
f2a38c9024
|
|
@ -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.
|
||||
|
|
@ -1435,7 +1435,6 @@
|
|||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
|
|
@ -1913,7 +1912,6 @@
|
|||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -2053,7 +2051,6 @@
|
|||
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
@ -2128,7 +2125,6 @@
|
|||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz",
|
||||
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.25",
|
||||
"@vue/compiler-sfc": "3.5.25",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
"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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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