ezmcp Releases (1): 0.1.0.0 0xe8a45a5d2dbc02504d10df4cabf172ea indexed https://jend.leeeet.dev/files.txt?path=ezmcp@0.1.0.0 Definitions (416, 1 to 100 shown): Ezmcp.tool (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=tool) # a tool: its name, a description for the model, and the JSON Schema of its # arguments (an object schema) def tool(name: String, description: String, input: Value.Json) -> Server.Tool: # noqa: L001 the Server.Tool constructor Ezmcp.server (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=server) # a server: name and version (serverInfo), instructions for the client ("" for # none), and its tools in the order tools/list shows them def server( # noqa: L001 the Server.Server constructor name: String, version: String, instructions: String, tools: List<&2, Server.Tool> ) -> Server.Server: Ezmcp.latest (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=latest) # the latest protocol revision ezmcp speaks def latest() -> String: # noqa: L001 re-export of Server.latest Ezmcp.revisions (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=revisions) # the protocol revisions ezmcp speaks, oldest first def revisions() -> List<&2, String>: # noqa: L001 re-export of Server.revisions Ezmcp.step (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=step) # what a server does with one message's text, read by revision `rev`: Quiet # (send nothing), Send or Fault (send this reply; a Fault is a message it # could not accept), Run (run a tool, then send `ran` of its result), or, # under 2025-03-26, Batch (do each of its acts, then send `batch.reply` of # their replies). For a transport of your own def step(srv: Server.Server, rev: String, text: String) -> Server.Act: # noqa: L001 re-export of Server.step Ezmcp.turn (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=turn) # `step`'s act, and the revision a stream's next message is read by: the one # an initialize was answered with, else `rev`. For a stream transport of your # own, which starts at `latest()` def turn(srv: Server.Server, rev: String, text: String) -> Server.Turn: # noqa: L001 re-export of Server.turn Ezmcp.ran (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=ran) # the reply to a tool call with this id under revision `rev`, once the # runner answered (2025-03-26 leaves structuredContent out) def ran(rev: String, id: Value.Json, out: Rpc.Out) -> Value.Json: # noqa: L001 re-export of Server.ran Ezmcp.batch.reply (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=batch.reply) # what a batch sends back, from its acts' replies in order (None for an act # with none): nothing when none has one, else their array def batch.reply(replies: List<&2, Maybe<&2, Value.Json>>) -> Maybe<&2, Value.Json>: Ezmcp.ok (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=ok) # a runner's answer for a tool that ran: its tool result, which may itself # report a failure with `isError` (`result.error`) def ok(result: Value.Json) -> Rpc.Out: # noqa: L001 the Rpc.Ok constructor Ezmcp.refuse (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=refuse) # a runner's answer for a call it refuses as a protocol error: a JSON-RPC # error with this code and message instead of a tool result # (server/tools#error-handling) def refuse(code: Rpc.Code, message: String) -> Rpc.Out: # noqa: L001 the Rpc.Err constructor Ezmcp.refuse.params (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=refuse.params) # refuse with -32602 Invalid params, as for arguments that do not match the # tool's input schema def refuse.params(message: String) -> Rpc.Out: Ezmcp.refuse.internal (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=refuse.internal) # refuse with -32603 Internal error, as for a server that cannot run the tool def refuse.internal(message: String) -> Rpc.Out: Ezmcp.text (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=text) # a text content block def text(text2: String) -> Value.Json: # noqa: L001 re-export of Server.text Ezmcp.result (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=result) # a tool result from content blocks; is_error flags a tool that failed, so # the model sees the failure (server/tools#error-handling) def result(blocks: List<&2, Value.Json>, is_error: Bool) -> Value.Json: # noqa: L001 re-export of Server.result Ezmcp.result.text (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=result.text) # a tool result of one text block def result.text(text: String) -> Value.Json: Ezmcp.result.error (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=result.error) # a failed tool's result: one text block, isError true def result.error(text: String) -> Value.Json: Ezmcp.result.json (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=result.json) # a tool result with structured content, and its JSON text as a text block; # a 2025-03-26 client gets the text block alone def result.json(val: Value.Json) -> Value.Json: Ezmcp.http.decide (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.decide) # what the HTTP transport decides for one request to an endpoint at `path` # that allows the browser origins listed: a reply now, or a tool to run def http.decide( srv: Server.Server, path: String, origins: List<&2, String>, req: Http.Request ) -> Web.Web: Ezmcp.http.handle (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.handle) # one request to the MCP endpoint at `path`, as an ezhttp IO handler, for a # server that serves other paths too def http.handle( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, req: Http.Request ) -> IO(Http.Reply): Ezmcp.http.serve (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve) # serve the endpoint on host:port for `limit` connections, each on its own # fork with no limit on how many at once, then stop once every connection is # closed def http.serve( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, host: String, port: U32, limit: U32 ) -> IO(Unit): Ezmcp.http.serve_forever (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve_forever) # serve the endpoint on host:port until the process is stopped, each # connection on its own fork with no limit on how many at once @unsafe def http.serve_forever( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, host: String, port: U32 ) -> IO(Unit): Ezmcp.http.handle_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.handle_with) # one request to the MCP endpoint at `path`, its runner given `ctx` def http.handle_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, req: Http.Request ) -> IO(Http.Reply): Ezmcp.http.serve_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve_with) # serve the endpoint on host:port for `limit` connections, its runner given # `ctx`, each connection on its own fork and at most `max` at once (0: no # limit; 1: one at a time), then stop once every connection is closed def http.serve_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, … Ezmcp.http.serve_forever_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve_forever_with) # serve the endpoint on host:port, its runner given `ctx`, each connection on # its own fork and at most `max` at once (0: no limit; 1: one at a time), # until the process is stopped @unsafe def http.serve_forever_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, … Ezmcp.http.handle_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.handle_from) # one request to the MCP endpoint at `path`, its server and runner given # `ctx` def http.handle_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, req: Http.Request ) -> IO(Http.Reply): Ezmcp.http.serve_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve_from) # serve the endpoint on host:port for `limit` connections, its server and # runner given `ctx`, each connection on its own fork and at most `max` at # once (0: no limit), then stop once every connection is closed def http.serve_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, … Ezmcp.http.serve_forever_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=http.serve_forever_from) # serve the endpoint on host:port, its server and runner given `ctx`, each # connection on its own fork and at most `max` at once (0: no limit), until # the process is stopped @unsafe def http.serve_forever_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, … Ezmcp.stdio.serve_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=stdio.serve_with) # serve stdin and stdout, its runner given `ctx`, until stdin ends @unsafe def stdio.serve_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C ) -> IO(Unit): Ezmcp.stdio.serve (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=stdio.serve) # serve newline-delimited JSON-RPC on stdin and stdout until stdin ends; # write logs with IO.print_err, never to stdout @unsafe def stdio.serve( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server ) -> IO(Unit): Ezmcp.openapi.ops (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.ops) # every operation of an OpenAPI document (a JSON value), in the order of # its paths, each path's in the order get, put, post, delete, options, # head, patch, trace; its local references (`#/...`) followed def openapi.ops(doc: Value.Json) -> List<&2, Openapi.Op>: Ezmcp.openapi.tool (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.tool) # an operation's tool: its name, description and input schema def openapi.tool(op: Openapi.Op) -> Server.Tool: Ezmcp.openapi.name (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.name) # an operation's tool name: its operationId as a tool name def openapi.name(op: Openapi.Op) -> String: Ezmcp.openapi.renamed (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.renamed) # an operation under another tool name, for a client that limits names def openapi.renamed(op: Openapi.Op, name: String) -> Openapi.Op: Ezmcp.openapi.check (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.check) # what is wrong with a call's arguments for an operation's input schema, # when anything is: Some of the message, or None when they fit def openapi.check(op: Openapi.Op, args: Value.Json) -> Maybe<&2, String>: Ezmcp.openapi.call (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=openapi.call) # the request a call of an operation with these arguments stands for: # Send{method, target, headers, body} (the target is the path and query, # for the caller to append to the API's base URL), or Unfilled{path} when a # path parameter has no argument def openapi.call(op: Openapi.Op, args: Value.Json) -> Openapi.Call: Ezmcp.client.initialize (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.initialize) # the initialize request, naming the client def client.initialize( id: Value.Json, name: String, version: String ) -> Value.Json: Ezmcp.client.initialized (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.initialized) # the notification to send once initialize succeeded def client.initialized() -> Value.Json: Ezmcp.client.tools (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.tools) # a tools/list request def client.tools(id: Value.Json) -> Value.Json: Ezmcp.client.call (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.call) # a tools/call request def client.call(id: Value.Json, name: String, args: Value.Json) -> Value.Json: Ezmcp.client.read (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.read) # what a reply's JSON text says: Got{id, result}, Refused{id, code, # message} or Unread{why} def client.read(text: String) -> Rpc.Reply: Ezmcp.client.version (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.version) # the revision an initialize result names, "" when it names none def client.version(result: Value.Json) -> String: Ezmcp.client.ask (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.ask) # POST a request to an MCP endpoint and read its reply, JSON or an event # stream. `version` is the negotiated revision ("" before initialize); the # timeout bounds connecting, sending and receiving def client.ask( url: String, version: String, timeout_ms: U32, msg: Value.Json ) -> IO(Rpc.Reply): Ezmcp.client.tell (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/main.bend&name=client.tell) # POST a notification; whether the server accepted it (any 2xx) def client.tell( url: String, version: String, timeout_ms: U32, msg: Value.Json ) -> IO(Bool): Client.init.params (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=init.params) # the params of initialize: the latest revision, no client capabilities, # and the client's name and version (basic/lifecycle#initialization) def init.params(name: String, version: String) -> Value.Json: Client.initialize (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=initialize) # the initialize request def initialize(id: Value.Json, name: String, version: String) -> Value.Json: Client.initialized (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=initialized) # the notification a client sends once initialize succeeded def initialized() -> Value.Json: # noqa: L001 closed constant; notice_no_id holds for every notification Client.tools (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=tools) # a tools/list request def tools(id: Value.Json) -> Value.Json: Client.call (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=call) # a tools/call request def call(id: Value.Json, name: String, args: Value.Json) -> Value.Json: Client.headers (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=headers) # the headers of every POST: a JSON body, both reply types accepted, and # the negotiated revision once there is one (empty before initialize) def headers(+version: String) -> List<&2, Http.Header>: Client.is.sse (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=is.sse) # whether a Content-Type is an event stream def is.sse(+ctype: String) -> Bool: Client.body (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=body) # a reply body's JSON text: an event stream's last data, else the body def body(sse: Bool, +text: String) -> String: Client.took.ok (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=took.ok) def took.ok(+ctype: String, text: String, ok: Bool, +status: U32) -> Rpc.Reply: Client.took (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=took) # what an HTTP reply to a request says: a 200 is read as JSON or as an event # stream by its Content-Type; any other status is unread def took(+status: U32, ctype: String, text: String) -> Rpc.Reply: Client.accepted (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=accepted) # whether an HTTP reply to a notification accepted it: any 2xx def accepted(+status: U32) -> Bool: # noqa: L001 only src/io.bend calls it (MCP-TRUST-1) Client.version (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/client.bend&name=version) # the revision an initialize result names, "" when it names none def version(result: Value.Json) -> String: Io.act.reply (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=act.reply) # one element of a batch, under revision `rev`: its reply, after running its # tool when it is a tool call; none for a notification or a response def act.reply( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), +ctx: C, +rev: String, act: Server.Act ) -> IO(Maybe<&2, Value.Json>): Io.act.all (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=act.all) # every element of a batch, in order def act.all( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), +ctx: C, +rev: String, acts: List<&2, Server.Act> ) -> IO(List<&2, Maybe<&2, Value.Json>>): Io.http.web (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.web) # act on the transport's decision: reply now, or run the tool and reply # with its result, or run a batch's acts and reply with their replies def http.web( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), +ctx: C, wid: Web.Web ) -> IO(Http.Reply): Io.http.handle_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.handle_with) # one HTTP request to a server's endpoint at `path` def http.handle_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, req: Http.Request ) -> IO(Http.Reply): Io.http.answer (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.answer) # the endpoint's answer to one request ezhttp read, as its plain serves # take one: the request as text in, a text reply out def http.answer( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, req: EzServer.Req ) -> IO(EzServer.Answer): Io.http.serve_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve_with) # serve `limit` connections, at most `max` at once (0: no limit), then stop def http.serve_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, port: U32, limit: U32, … Io.http.serve_forever_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve_forever_with) # serve until the process is stopped, at most `max` connections at once # (0: no limit) @unsafe def http.serve_forever_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, port: U32, … Io.http.answer_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.answer_from) # the endpoint's answer to one request ezhttp read, its server read from the # context def http.answer_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, req: EzServer.Req ) -> IO(EzServer.Answer): Io.http.serve_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve_from) # serve `limit` connections, at most `max` at once (0: no limit), then stop def http.serve_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, port: U32, limit: U32, … Io.http.serve_forever_from (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve_forever_from) # serve until the process is stopped, at most `max` connections at once # (0: no limit) @unsafe def http.serve_forever_from( ~C: Data, ~srv: C -> Server.Server, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~path: String, ~origins: List<&2, String>, +ctx: C, host: String, port: U32, … Io.http.handle (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.handle) # one HTTP request to a server's endpoint at `path` def http.handle( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, req: Http.Request ) -> IO(Http.Reply): Io.http.serve (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve) # serve `limit` connections, as many at once as arrive, then stop def http.serve( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, host: String, port: U32, limit: U32 ) -> IO(Unit): Io.http.serve_forever (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=http.serve_forever) # serve until the process is stopped, as many connections at once as arrive @unsafe def http.serve_forever( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, ~path: String, ~origins: List<&2, String>, host: String, port: U32 ) -> IO(Unit): Io.ezmcp.stdin (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=ezmcp.stdin) # up to `max` bytes from file descriptor 0, as one read(2) returns them; # empty at the end of input (MCP-TRUST-5) def ezmcp.stdin(max: U32) -> IO(Result<&1, &1, U32 & String, List<&2, U32>>): Io.stdio.chunk (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.chunk) # bytes one read of stdin asks for def stdio.chunk() -> U32: Io.stdio.batch (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.batch) # a batch's replies: nothing when there are none, else one line def stdio.batch(replies: Maybe<&2, Value.Json>) -> IO(Unit): Io.stdio.act (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.act) # act on one line's act, read by revision `rev`: write a reply as one line, # or run the tool and write its reply, or run a batch's acts and write the # array of their replies, or write nothing def stdio.act( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), +ctx: C, +rev: String, act: Server.Act ) -> IO(Unit): Io.stdio.turn (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.turn) # act on a turn read by revision `rev`, then go on with the revision after it def stdio.turn( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), +ctx: C, +rev: String, turn: Server.Turn, next: String -> IO(String) ) -> IO(String): Io.stdio.lines (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.lines) # every whole line, in order, each read by the revision the line before it # left; answers the revision the last one left def stdio.lines( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C, lines: List<&2, List<&2, U32>>, +rev: String ) -> IO(String): Io.stdio.cut (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.cut) # act on a cut's whole lines, then read on with its rest def stdio.cut( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C, rev: String, again: List<&2, U32> -> String -> IO(Unit), cut: Line.Cut ) -> IO(Unit): Io.stdio.chunked (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.chunked) # after a read: an empty one is the end of stdin, and the bytes after the # last newline are the last message; any other is cut into lines def stdio.chunked( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C, rev: String, buf: List<&2, U32>, bytes: List<&2, U32>, again: List<&2, U32> -> String -> IO(Unit), … Io.stdio.got (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.got) # after one read: a failure is logged to stderr and ends the loop def stdio.got( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C, rev: String, buf: List<&2, U32>, again: List<&2, U32> -> String -> IO(Unit), res: Result<&1, &1, U32 & String, List<&2, U32>> ) -> IO(Unit): Io.stdio.loop (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.loop) # read, act on every whole line, and read again until stdin ends; `rev` is # the revision the next line is read by @unsafe def stdio.loop( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C, buf: List<&2, U32>, rev: String ) -> IO(Unit): Io.stdio.serve_with (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.serve_with) # serve newline-delimited JSON-RPC on stdin and stdout until stdin ends; a # line is read by the latest revision until an initialize names another @unsafe def stdio.serve_with( ~C: Data, ~run: C -> String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server, +ctx: C ) -> IO(Unit): Io.stdio.serve (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=stdio.serve) # serve newline-delimited JSON-RPC on stdin and stdout until stdin ends, # with no context @unsafe def stdio.serve( ~run: String -> Value.Json -> IO(Rpc.Out), ~srv: Server.Server ) -> IO(Unit): Io.client.took (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=client.took) # what an ezhttp response to a request says def client.took(res: EzClient.Response) -> Rpc.Reply: Io.client.ask (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=client.ask) # POST one request and read its reply def client.ask( url: String, version: String, timeout_ms: U32, msg: Value.Json ) -> IO(Rpc.Reply): Io.client.sent (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=client.sent) def client.sent(res: EzClient.Response) -> Bool: Io.client.tell (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/io.bend&name=client.tell) # POST one notification; whether the server accepted it def client.tell( url: String, version: String, timeout_ms: U32, msg: Value.Json ) -> IO(Bool): Line.Cut (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=Cut) # whole lines, each with its newline, and the bytes after the last newline type Cut is Data: Cut{lines: List<&2, List<&2, U32>>, rest: List<&2, U32>} Line.nl (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=nl) # a byte is a newline def nl(+byte: U32) -> Bool: Line.split.put (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=split.put) # put a byte at the front of the first piece def split.put(byte: U32, ps: List<&2, List<&2, U32>>) -> List<&2, List<&2, U32>>: Line.split.at (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=split.at) # a newline ends a piece of its own, which the bytes before it join; any # other byte joins the first piece def split.at( byte: U32, ps: List<&2, List<&2, U32>>, cut: Bool ) -> List<&2, List<&2, U32>>: Line.split (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=split) # bytes split after every newline: each piece but the last ends with its # newline, and the last has none def split(bs: List<&2, U32>) -> List<&2, List<&2, U32>>: Line.cut.cons (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=cut.cons) def cut.cons(piece: List<&2, U32>, cb: Cut) -> Cut: Line.cut.go (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=cut.go) def cut.go(rest: List<&2, List<&2, U32>>, piece: List<&2, U32>) -> Cut: Line.cut.of (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=cut.of) def cut.of(ps: List<&2, List<&2, U32>>) -> Cut: Line.cut (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=cut) # the whole lines in a buffer, and what follows the last newline def cut(bs: List<&2, U32>) -> Cut: Line.cont (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=cont) # a byte is a continuation byte, 0x80 to 0xBF def cont(+byte: U32) -> Bool: Line.bad (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=bad) # U+FFFD, for a byte that starts no valid sequence def bad() -> Char: Line.low6 (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=low6) # a continuation byte's six payload bits def low6(+byte: U32) -> U32: Line.two (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=two) # the code point of a two-byte sequence def two(+lead: U32, +byte: U32) -> U32: Line.three (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=three) # the code point of a three-byte sequence def three(+lead: U32, +byte: U32, +cb: U32) -> U32: Line.four (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=four) # the code point of a four-byte sequence def four(+lead: U32, +byte: U32, +cb: U32, +db: U32) -> U32: Line.Width (https://jend.leeeet.dev/definition.txt?path=ezmcp@0.1.0.0/src/line.bend&name=Width) # how many bytes a lead byte starts type Width is Data: W0{} W1{} W2{} W3{} W4{} More: https://jend.leeeet.dev/packages/ezmcp.txt?definitions_offset=100