{
 "openapi": "3.1.0",
 "info": {
  "title": "Cyclone Machining Co. - the agent door",
  "version": "2026-10-09",
  "description": "A machine-readable quote request intake for a prototype and R&D machine shop in Grand Rapids, Michigan. One part is a real job here. Send what you have; a person reads it before anything is priced. Every request becomes one mail to the shop's inbox with a request id; nothing is stored on the host, and the request id is the receipt. The same path is an MCP server and an A2A agent (see x-mcp and x-a2a).",
  "contact": {
   "name": "Cyclone Machining Co.",
   "url": "https://cyclonemachining.com/rfq",
   "email": "rfq@cyclonemachining.com"
  }
 },
 "servers": [
  {
   "url": "https://for.cyclonemachining.com"
  }
 ],
 "paths": {
  "/api/rfq": {
   "get": {
    "summary": "The schema of this door",
    "operationId": "getSchema",
    "responses": {
     "200": {
      "description": "The fields, the reply's shape, the MCP tools and the A2A methods, as JSON",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/DoorSchema"
        }
       }
      }
     },
     "405": {
      "description": "A GET that accepts only text/event-stream (an MCP client asking for a server stream): none is offered"
     }
    }
   },
   "post": {
    "summary": "File a quote request",
    "operationId": "fileQuoteRequest",
    "description": "Files one request: one mail to the shop with a request id. The same path also takes JSON-RPC 2.0 bodies (MCP and A2A); see x-mcp and x-a2a.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/QuoteRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Filed",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Receipt"
        }
       }
      }
     },
     "400": {
      "description": "A required field is missing or malformed, or export_controlled is not one of no, yes, not sure; nothing was filed",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "More than 20 requests a minute from one address; nothing was filed",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "500": {
      "description": "The door is not wired on this host; nothing was filed",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The mail to the shop could not be sent; nothing was filed",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "QuoteRequest": {
    "type": "object",
    "required": [
     "name",
     "email",
     "description"
    ],
    "properties": {
     "name": {
      "type": "string",
      "description": "required, the person"
     },
     "email": {
      "type": "string",
      "description": "required, where the shop's reply goes",
      "format": "email"
     },
     "company": {
      "type": "string",
      "description": "optional"
     },
     "material": {
      "type": "string",
      "description": "optional, e.g. 6061 aluminum, 17-4 stainless, titanium, Inconel 718"
     },
     "quantity": {
      "type": "integer",
      "minimum": 1,
      "description": "optional, one is normal here"
     },
     "needed_by": {
      "type": "string",
      "description": "optional, ISO date",
      "format": "date"
     },
     "description": {
      "type": "string",
      "maxLength": 8000,
      "description": "required, up to 8,000 characters: what the part does, what to hold, what exists (a STEP, a PDF, a broken sample)"
     },
     "files": {
      "type": "array",
      "description": "optional, [{name, bytes}] - names only; the answer to the request says where to send the files (no file link when export_controlled is yes or not sure)",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "bytes": {
         "type": "integer"
        }
       }
      }
     },
     "no_drawing": {
      "type": "boolean",
      "description": "optional, true when the part itself is the drawing"
     },
     "agent": {
      "type": "string",
      "description": "optional, the name and version of the agent filing this"
     },
     "review_only": {
      "type": "boolean",
      "description": "optional, true to ask for a free engineer's review of the part: no quote is sent unless asked"
     },
     "sent_back": {
      "type": "boolean",
      "description": "optional, true when another shop sent the job back"
     },
     "sent_back_reason": {
      "type": "string",
      "maxLength": 500,
      "description": "optional, up to 500 characters: why it was sent back (a reason alone marks the request as sent back)"
     },
     "export_controlled": {
      "type": "string",
      "enum": [
       "no",
       "yes",
       "not sure"
      ],
      "description": "optional, one of no, yes, not sure: whether the part's data is export-controlled (ITAR or EAR). Yes or not sure: the answer gives no file link, and no files or controlled detail are sent until a person arranges a controlled channel"
     }
    }
   },
   "Receipt": {
    "type": "object",
    "required": [
     "ok",
     "request_id",
     "received",
     "next",
     "note"
    ],
    "properties": {
     "ok": {
      "type": "boolean",
      "const": true
     },
     "request_id": {
      "type": "string",
      "pattern": "^CM-AGENT-[0-9]{8}-[0-9A-F]{6}$",
      "examples": [
       "CM-AGENT-20261009-A1B2C3"
      ]
     },
     "received": {
      "type": "string",
      "format": "date-time"
     },
     "next": {
      "type": "string"
     },
     "note": {
      "type": "string",
      "description": "the answer in words: the request id, where the reply goes, and where to send the files or why not yet"
     },
     "files_link": {
      "type": "string",
      "format": "uri",
      "description": "where to send the files; absent when export_controlled is yes or not sure"
     },
     "mail_id": {
      "type": "string",
      "description": "the id of the mail to the shop"
     }
    }
   },
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string"
     },
     "schema": {
      "type": "string",
      "description": "where the schema is"
     },
     "status": {
      "type": "integer",
      "description": "the mail provider's status, on 502"
     }
    }
   },
   "DoorSchema": {
    "type": "object",
    "required": [
     "name",
     "version",
     "post",
     "fields"
    ],
    "properties": {
     "name": {
      "type": "string"
     },
     "version": {
      "type": "string"
     },
     "what_it_is": {
      "type": "string"
     },
     "post": {
      "type": "string"
     },
     "fields": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      }
     },
     "reply": {
      "type": "object"
     },
     "mcp": {
      "type": "object"
     },
     "a2a": {
      "type": "object"
     },
     "openapi": {
      "type": "string"
     },
     "human_site": {
      "type": "string"
     },
     "llms": {
      "type": "string"
     }
    }
   }
  }
 },
 "x-mcp": {
  "transport": "streamable-http",
  "endpoint": "https://for.cyclonemachining.com/api/rfq",
  "protocolVersion": "2025-06-18",
  "tools": [
   {
    "name": "file_quote_request",
    "title": "File a quote request with Cyclone Machining Co.",
    "readOnlyHint": false
   },
   {
    "name": "get_door_schema",
    "title": "Read the door's schema",
    "readOnlyHint": true
   }
  ],
  "note": "The same path answers JSON-RPC 2.0: initialize, ping, tools/list, tools/call; notifications are acknowledged 202; a GET that accepts only text/event-stream is answered 405."
 },
 "x-a2a": {
  "card": "https://for.cyclonemachining.com/.well-known/agent-card.json",
  "endpoint": "https://for.cyclonemachining.com/api/rfq",
  "protocolBinding": "JSONRPC",
  "versions": {
   "0.3": [
    "message/send",
    "tasks/get"
   ],
   "1.0": [
    "SendMessage",
    "GetTask"
   ]
  },
  "note": "a data part carries these fields and a text part becomes the description; the answer is a completed task whose id is the request id"
 },
 "x-human": {
  "site": "https://cyclonemachining.com/rfq",
  "llms": "https://cyclonemachining.com/llms.txt",
  "cite": "https://for.cyclonemachining.com/cite/"
 }
}
