Resource Access API
Table of Contents
The backend exposes its model over the Resource Access, the connection the extension opens with Actifsource: Connect Resource Access. Everything the extension does - reading the model, changing it, searching, generating - goes through it. The interface is open: any client that speaks it can work on the same model and the same session state.
This chapter describes the protocol itself. The individual requests are listed in the specification.
This is the interface the extension uses, not a stable public contract. It changes with the protocol version - see Protocol version.
Connecting
The backend listens on a WebSocket. Host and port are the ones configured for the extension,
localhost and 8088 by default:
ws://<host>:<port>Connect to the bare path. The path of the URL becomes a prefix of every method name, so a
connection to ws://host:port/foo addresses the services foo/SelectJob, foo/Session and so on -
and nothing answers them. The only user of a prefix is the MCP server under /mcp, which is a
separate HTTP interface.
A text frame carries at most 5 MiB. The connection has no idle timeout. There is no handshake, no session id and no authentication: the first frame after the connection is open may already be a request.
The envelope
All traffic is JSON-RPC 2.0. A request names the service in
method and carries an integer id:
{ "jsonrpc": "2.0", "id": 42, "method": "SelectJob", "params": { } }{ "jsonrpc": "2.0", "id": 42, "result": { } }The id must be a JSON number. A string id is read as absent, the frame is treated as a
notification, and no answer is ever sent.
params always carries a type field that selects the request within the service. The services
are:
method |
Purpose |
|---|---|
SelectJob |
Reading the model - one request type per select job |
UpdateJob |
Changing the model - one request type per update job |
State |
Asking for what changed since the last call (long poll) |
Session |
Save, undo, redo, revert, and forks of the session |
Search |
Searching the model |
Command |
Executing a model command, e.g. the code generator |
Selection |
The shared selection between the clients of a session |
Link |
Resolving a link to a resource |
AutoCompletion |
Content proposals for the template editor |
ConsoleLogger |
The log and the error reporter |
Version |
The protocol version of the backend |
An unknown method is answered - see Errors - with
Request '<method>' is not handled: <the original frame>.
Reading the model
A select job is a request to SelectJob with type: "Selectable". The job itself sits in data,
where its own type names the job:
{ "jsonrpc": "2.0", "id": 42, "method": "SelectJob",
"params": {
"type": "Selectable",
"editContext": { "type": "GlobalEditContext" },
"dependencyId": 7,
"data": {
"type": "SelectSimpleName",
"node": { "type": "Resource", "guid": { "uuid": "4cf5ec36-..." } }
}
}
}| Key | Required | Meaning |
|---|---|---|
type |
yes | "Selectable" |
data |
yes | The job and its arguments, type names the job |
editContext |
yes | What the read sees - GlobalEditContext is the whole workspace |
dependencyId |
no | Marks the read, so that the client learns when its result goes stale |
forkId |
no | Reads the fork instead of the session |
The answer wraps the job’s result the same way:
{ "jsonrpc": "2.0", "id": 42,
"result": { "type": "Selectable", "data": { "type": "SelectSimpleName", "name": "Engine" } }
}Streaming
A job that answers in several pieces puts a streamingId next to its data. The client asks for
the next piece with a NextChunk request, and keeps asking as long as the answer carries a
streamingId:
{ "jsonrpc": "2.0", "id": 43, "method": "SelectJob",
"params": { "type": "NextChunk", "streamingId": 12, "canceled": false }
}An answer without streamingId is the last one. That is the only end-of-stream signal. To stop
early, send canceled: true on the next NextChunk.
Staying up to date
A select result is a snapshot. A client that wants to know when the model has moved under it passes
a dependencyId of its own choosing with the request. The backend remembers what the read touched,
and reports once when any of it changes.
The report is not pushed. The client collects it from the State service, which holds the request
open until something has changed:
{ "jsonrpc": "2.0", "id": 99, "method": "State",
"params": { "type": "ResourceAccessObserver", "ids": ["SelectJob"], "force": false }
}{ "jsonrpc": "2.0", "id": 99,
"result": { "type": "ResourceAccessObserver",
"states": [ { "type": "DependencyChanged", "serviceId": "SelectJob", "dependencyId": 7 } ] }
}| Key | Required | Meaning |
|---|---|---|
ids |
yes | The services to observe. Nothing is collected for a service never asked for |
max |
no | At most this many states per answer |
force |
no | true answers at once, with an empty list if nothing changed |
A dependency fires once and is then forgotten. A client that wants to keep watching re-sends the
select job with a new dependencyId.
Changing the model
An update job is a request to UpdateJob with type: "Modifiable":
{ "jsonrpc": "2.0", "id": 43, "method": "UpdateJob",
"params": {
"type": "Modifiable",
"editContext": { "type": "GlobalEditContext" },
"autoSave": true,
"data": { "type": "UpdateName", "resource": { }, "name": "Engine" }
}
}It differs from a select job in three points: autoSave is required, there is no
dependencyId (an update does not observe anything), and the answer is not wrapped. The job’s
result is the result itself:
{ "jsonrpc": "2.0", "id": 43, "result": { "type": "UpdateName" } }autoSave: false leaves the change in the session, to be written by Session / Save - or
dropped by Revert.
Errors
A failed request is answered with a successful JSON-RPC response. The WebSocket transport never
sends a JSON-RPC error member; the failure sits in result, under error:
{ "jsonrpc": "2.0", "id": 42,
"result": { "error": { "name": "ResourceNotExistException",
"message": "Resource Engine does not exist!",
"stack": "..." } }
}A client decides that a request failed by the presence of result.error, not by the shape of the
frame. stack is optional. name is one of:
name |
Meaning |
|---|---|
Error |
Anything the backend does not classify further |
ResourceAccessException |
The request could not be handled, e.g. an unknown method |
ConnectionException |
The backend is shutting down |
ResourceNotExistException |
The addressed resource does not exist (any more) |
RootResourceNotExistException |
The addressed root resource does not exist (any more) |
NoOperationException |
Undo or redo with nothing to undo or redo |
The numeric JSON-RPC error codes (-32700, -32602, -32603) occur only on the MCP interface.
Protocol version
The backend answers its protocol version:
{ "jsonrpc": "2.0", "id": 1, "method": "Version", "params": { "type": "ProtocolVersion" } }{ "jsonrpc": "2.0", "id": 1, "result": { "type": "Version", "version": "1.0.0.+390187895" } }The extension asks this first and disconnects on any difference - the strings must be equal, the version is not a range. A client of its own is not forced to check, but a version it was not written against gives no guarantee about any of the requests below.
Specification
The full request and response of every job and service is specified in AsyncAPI and listed on the pages below. The specification is generated from the backend, so it describes the version of the backend it ships with.