MCS Engine: SDK API Reference
Osinix (osinix.com)
This is the function-level reference for the MCS SDK. For installation, linking, and a first working program, see the SDK Getting Started guide. For the deliberation concepts and the session config format, see the User Guide.
Each entry below gives the C function and its Python equivalent side by side, with arguments, return values, and the errors it can produce.
Conventions
Return values (C). Every C function returns MCS_OK (0) on success and a
negative MCS_ERR_* code on failure. Functions that return a pointer return
NULL on failure. Functions that return a count or a byte length return a
non-negative number on success.
Error reporting (C). After any failed call, mcs_errno() holds the numeric
code and mcs_error() holds a human-readable message. A successful call does not
clear them, so check each call's own return value rather than polling the globals.
Errors (Python). The Python SDK raises exceptions instead of returning codes:
MCSError (base), and MCSLicenseError, MCSTimeoutError, MCSConfigError.
The session handle. mcs_session_start returns an opaque handle. You own it
from that moment and must call mcs_session_free to release it, whatever the
outcome. In Python the Session object owns the handle and frees it on context
exit.
One session per process. Only one session runs at a time, enforced by a
lockfile. A second concurrent start returns MCS_ERR_SESSION_ALREADY_RUNNING.
Thread safety. The error-state functions (mcs_errno, mcs_error) use
process-global state and are not thread-safe. Treat one session and its handle as
owned by one thread.
Library lifecycle
Initialize the library
int mcs_library_init(const char *engine_path, const char *license_path);
mcs.init(license_path=None, engine_path=None) # raises on failure
Loads and verifies the license, caches the licensed tier, and prepares internal
state. Call it once at program start. (If you never call it, the first
mcs_session_start initializes lazily.)
engine_path: where to find the engine binary.NULLsearchesPATHfor the tier-matching name (mcs_basic/mcs_pro/mcs_pro_max); a directory is searched for that binary; a file path is exec'd directly.license_path: thelicense.keyto use.NULLuses the standard search order (thelicense_pathargument, then$MCS_LICENSE_KEY, then the default config location).
Repeat calls are a smart merge: the license from the first successful call is
cached for the process and not reloaded; a later call may add an engine_path,
or pass NULL to keep what it has, but a later call with a different
license_path is rejected with MCS_ERR_INVALID_PARAMETER.
Returns MCS_OK, or MCS_ERR_LICENSE_INVALID, MCS_ERR_LICENSE_EXPIRED,
MCS_ERR_INVALID_PARAMETER, MCS_ERR_INTERNAL. (Python raises MCSLicenseError
for the license cases.)
Get the SDK version
const char *mcs_library_version(void);
mcs.version() # -> str
Returns the version string ("MAJOR.MINOR.PATCH"). Safe to call before init. The string is owned by the library in C; never free it.
License and locations
License information
int mcs_license_info(mcs_license_info_t *out);
mcs.license_info() # -> LicenseInfo(licensee, tier, expires, days_remaining, has_sdk)
Fills out from the license cached at init (no file I/O); unlike locations(),
no running session is required. In Python, has_sdk is returned as a bool.
typedef struct {
char licensee[256]; /* person or company on the license */
char tier[32]; /* "Basic" / "Pro" / "Pro Max" / "Enterprise" */
char expires[16]; /* "YYYY-MM-DD" */
int days_remaining; /* negative if already expired */
int has_sdk; /* 1 if this license grants SDK access */
} mcs_license_info_t;
Returns MCS_OK, MCS_ERR_NOT_INITIALIZED, or MCS_ERR_INVALID_PARAMETER
(out is NULL).
Resolved file locations
int mcs_locations(mcs_locations_t *out);
mcs.locations() # -> Locations(install_dir, config_path, sessions_dir, temp_dir)
Fills the engine's resolved paths: install_dir, config_path, sessions_dir,
temp_dir (each a NUL-terminated string in C). These are reported by the engine,
so config_path is empty until a config has been loaded. Returns MCS_OK, or
MCS_ERR_NOT_INITIALIZED / MCS_ERR_LOCATIONS_UNAVAILABLE /
MCS_ERR_NO_ACTIVE_SESSION when the paths are not yet available.
Session lifecycle
Start a session
mcs_session_handle_t *mcs_session_start(const char *mcs_conf_path,
const char *session_conf_path);
mcs.start(mcs_conf, session_conf) # -> Session
Forks and execs the tier-matching engine as a subprocess with your mcs.conf and
session config, then returns immediately with a handle. The license and tier
limits are checked before the subprocess is spawned, so an over-tier config fails
here rather than mid-run.
mcs_conf_path: path tomcs.conf. Must exist and be readable.session_conf_path: path to the session config (panel, agenda, mode).
Returns a non-NULL handle on success, or NULL with mcs_errno set to one of:
MCS_ERR_NOT_INITIALIZED, MCS_ERR_LICENSE_INVALID, MCS_ERR_LICENSE_EXPIRED,
MCS_ERR_FEATURE_NOT_LICENSED, MCS_ERR_ENGINE_BINARY_NOT_FOUND,
MCS_ERR_CONFIG_NOT_FOUND, MCS_ERR_CONFIG_INVALID,
MCS_ERR_TIER_LIMIT_EXCEEDED, MCS_ERR_SESSION_ALREADY_RUNNING,
MCS_ERR_SUBPROCESS_SPAWN_FAILED, MCS_ERR_INVALID_PARAMETER,
MCS_ERR_INTERNAL. (Python returns a Session or raises the matching exception.)
Wait for natural completion
int mcs_session_wait(mcs_session_handle_t *handle, int timeout_sec);
session.wait(timeout=None)
Blocks until the session ends on its own (moderator ended it, the limit was
reached, or an engine error). It does not signal the engine. Use 0 (C) or
None (Python) to wait indefinitely; a positive timeout returns
MCS_ERR_TIMEOUT (C) or raises MCSTimeoutError (Python) if the session is still
running when it elapses, leaving the session alive to wait on again or stop.
Returns MCS_OK, MCS_ERR_TIMEOUT, MCS_ERR_HANDLE_INVALID,
MCS_ERR_INVALID_PARAMETER (negative timeout), or MCS_ERR_INTERNAL.
Stop a session
int mcs_session_stop(mcs_session_handle_t *handle, int mode);
session.stop(force=False)
Ends a running session.
MCS_STOP_GRACEFUL(C) /force=False(Python): sends an interrupt; the engine finishes the current turn, writes its final transcript, summary, and status, then exits. Blocks until it exits or a 30-second grace cap, returningMCS_ERR_TIMEOUTif the engine has not exited (escalate to force).MCS_STOP_FORCE(C) /force=True(Python): kills the engine immediately; final output may be missing. Use for emergencies or after a graceful timeout.
Idempotent: stopping a finished session is a no-op that returns MCS_OK. Other
returns: MCS_ERR_HANDLE_INVALID, MCS_ERR_INVALID_PARAMETER (unknown mode),
MCS_ERR_IO.
Free the handle
void mcs_session_free(mcs_session_handle_t *handle);
session.free() # usually automatic via the context manager
Releases the handle and its resources. If the session is still running it is
stopped and reaped first, so the call may block briefly while the engine flushes.
After this the handle is invalid. mcs_session_free(NULL) is a safe no-op. In
Python, leaving a with mcs.start(...) as session: block frees it automatically.
Session monitoring
All monitoring calls read the engine's per-turn status.json in the session
directory. Very early in a session that file may not exist yet, in which case the
C calls return MCS_ERR_STATUS_FILE_MISSING; retry briefly or call
mcs_session_wait(handle, 1) to give the engine time to write it.
Is the session done?
int mcs_session_is_done(mcs_session_handle_t *handle);
session.is_done() # -> bool
Returns 1 if the session has reached a terminal phase (completed, stopped, or
error), 0 if still running; or MCS_ERR_HANDLE_INVALID,
MCS_ERR_STATUS_FILE_MISSING, MCS_ERR_IO.
Turn and round numbers
int mcs_session_get_turn(mcs_session_handle_t *handle, int which);
int mcs_session_get_round(mcs_session_handle_t *handle, int which);
/* convenience inlines: mcs_session_get_current_turn(h),
mcs_session_get_last_completed_turn(h), and the round equivalents */
session.current_turn() # -> int
session.last_completed_turn() # -> int
session.current_round() # -> int
session.last_completed_round() # -> int
which is MCS_CURRENT (the turn/round being processed now) or
MCS_LAST_COMPLETED (the most recent finished one, whose output file exists on
disk). Returns the number (>= 0), or MCS_ERR_HANDLE_INVALID,
MCS_ERR_INVALID_PARAMETER, MCS_ERR_STATUS_FILE_MISSING, MCS_ERR_IO.
Phase and status strings
int mcs_session_get_phase(mcs_session_handle_t *handle, char *out, size_t out_sz);
int mcs_session_get_status(mcs_session_handle_t *handle, char *out, size_t out_sz);
session.phase() # -> str
session.status() # -> str
phase is one of deliberation, completed, aborted (a 32-byte buffer is
safe). status is one of running, completed, stopped, error (a 16-byte
buffer is safe). The C calls copy the string into your buffer and return the byte
count written (excluding NUL), or MCS_ERR_HANDLE_INVALID,
MCS_ERR_INVALID_PARAMETER (NULL/zero/too-small buffer),
MCS_ERR_STATUS_FILE_MISSING, MCS_ERR_IO.
Files
The session keeps files in three categories. The C API exposes basenames; build an absolute path by joining the session directory, the category subfolder, and the basename. Python returns ready-to-use paths.
MCS_FILES_INPUT(input/): files you supplied before the session began.MCS_FILES_OUTPUT(output/): engine outputs (transcript, summary, metadata, per-turn files).MCS_FILES_MEDIA(media/): files models produced and exchanged during the session.
Session directory
int mcs_session_get_session_dir(mcs_session_handle_t *handle, char *out, size_t out_sz);
session.session_dir() # -> str
Copies the absolute session directory path. Returns bytes written, or
MCS_ERR_HANDLE_INVALID, MCS_ERR_INVALID_PARAMETER,
MCS_ERR_STATUS_FILE_MISSING, MCS_ERR_IO.
Listing files
int mcs_session_get_file_count(mcs_session_handle_t *handle, int category);
int mcs_session_get_file(mcs_session_handle_t *handle, int category,
int index, char *out, size_t out_sz);
/* convenience inlines exist per category, e.g.
mcs_session_get_output_file_count / mcs_session_get_output_file */
session.input_files() # -> list[str]
session.output_files() # -> list[str]
session.media_files() # -> list[str]
get_file_count returns the number of files in a category; get_file copies the
basename of the index-th file. Returns MCS_ERR_INVALID_PARAMETER for an
unrecognized category or out-of-range index, plus the usual
MCS_ERR_HANDLE_INVALID, MCS_ERR_STATUS_FILE_MISSING, MCS_ERR_IO. In C, join
session_dir + "/" + category-folder + "/" + basename for the full path.
Building a session config from JSON
int mcs_session_config_save_conf(const char *json, const char *out_path);
int mcs_session_config_save_json(const char *json, const char *out_path);
mcs.save_session_config_as_conf(json_str, out_path)
mcs.save_session_config_as_json(json_str, out_path)
Serialize a JSON session description (the same shape the web UI uses) to a .conf
or .json file you can then pass to start. The .conf form is preferred for
human editing. Returns MCS_OK, or MCS_ERR_INVALID_PARAMETER (NULL arguments),
MCS_ERR_CONFIG_INVALID (unparseable JSON or missing required fields), or
MCS_ERR_IO (could not write). You can also author the config file by hand in the
format documented in the User Guide and skip these entirely.
Error state (C)
int mcs_errno(void);
const char *mcs_error(void);
mcs_errno returns the most recent error code (MCS_OK if none since init).
mcs_error returns a human-readable description of the last error, owned by the
library. Both are overwritten only by later failed calls, not by successful ones.
The Python SDK surfaces this through exception types and messages instead.
Constants
All constants are plain integers. In C they carry the MCS_ prefix and are
defined in libmcs.h; in Python they are attributes on the mcs module with
the prefix dropped (for example MCS_CURRENT is mcs.CURRENT). Each group
below is the set of allowed values for one function argument.
Turn / round selectors
MCS_CURRENT 0 /* the in-progress turn or round */
MCS_LAST_COMPLETED 1 /* the most recently finished turn or round */
Passed as the which argument to mcs_session_get_turn() and
mcs_session_get_round() to say which number you want: the one being processed
right now (MCS_CURRENT), or the most recent one whose output file is already
on disk (MCS_LAST_COMPLETED). During an active session the current value is
one ahead of the last completed; after the session ends they are equal. In
Python, use the named methods instead: session.current_turn(),
session.last_completed_turn(), and the round equivalents.
For example:
int cur = mcs_session_get_turn(s, MCS_CURRENT);
int done = mcs_session_get_turn(s, MCS_LAST_COMPLETED);
cur = session.current_turn()
done = session.last_completed_turn()
File categories
MCS_FILES_INPUT 0 /* <session_dir>/input/ */
MCS_FILES_OUTPUT 1 /* <session_dir>/output/ */
MCS_FILES_MEDIA 2 /* <session_dir>/media/ */
Passed as the category argument to mcs_session_get_file_count() and
mcs_session_get_file() to choose which set of files to list: the inputs you
supplied before the session, the engine's structured outputs (transcript,
summary, metadata, per-turn files), or the media files the models produced and
exchanged. Each category maps to the matching subdirectory of the session
directory. In Python, use session.input_files(), session.output_files(),
and session.media_files().
For example, to list the output files:
char name[256];
int n = mcs_session_get_file_count(s, MCS_FILES_OUTPUT);
for (int i = 0; i < n; i++)
if (mcs_session_get_file(s, MCS_FILES_OUTPUT, i, name, sizeof name) >= 0)
printf("%s\n", name);
for name in session.output_files():
print(name)
Stop modes
MCS_STOP_GRACEFUL 0 /* interrupt; let the engine flush, 30s grace */
MCS_STOP_FORCE 1 /* kill immediately */
Passed as the mode argument to mcs_session_stop(). MCS_STOP_GRACEFUL lets
the engine finish the current turn and write its final transcript, summary, and
status before exiting (within a grace window; see mcs_session_stop above).
MCS_STOP_FORCE kills the engine immediately, so the final output files may be
missing. In Python, use session.stop(force=False) (graceful, the default) or
session.stop(force=True).
For example, stop gracefully and escalate to force if it does not exit in time:
if (mcs_session_stop(s, MCS_STOP_GRACEFUL) == MCS_ERR_TIMEOUT)
mcs_session_stop(s, MCS_STOP_FORCE);
try:
session.stop() # graceful (force=False)
except mcs.MCSTimeoutError:
session.stop(force=True)
Error codes
MCS_OK is 0; all errors are negative. The Value column is the exact integer
a C function returns (and what the matching mcs.ERR_* constant equals in
Python), so a raw return value like -10 can be mapped back to its meaning.
| Code | Value | Meaning |
|---|---|---|
MCS_OK |
0 |
Success |
MCS_ERR_NOT_INITIALIZED |
-1 |
mcs_library_init has not succeeded |
MCS_ERR_LICENSE_INVALID |
-2 |
License missing, malformed, or bad signature |
MCS_ERR_LICENSE_EXPIRED |
-3 |
License valid but past expiry |
MCS_ERR_FEATURE_NOT_LICENSED |
-18 |
Valid license, but no SDK access |
MCS_ERR_ENGINE_BINARY_NOT_FOUND |
-4 |
No matching engine binary found |
MCS_ERR_CONFIG_NOT_FOUND |
-5 |
A config file path does not exist |
MCS_ERR_CONFIG_INVALID |
-6 |
A config file is malformed |
MCS_ERR_TIER_LIMIT_EXCEEDED |
-16 |
Config exceeds the tier's limits |
MCS_ERR_FILE_FORMAT_NOT_ALLOWED |
-19 |
A file type the tier cannot use |
MCS_ERR_SESSION_ALREADY_RUNNING |
-7 |
Another session is active |
MCS_ERR_SESSION_ALREADY_DONE |
-17 |
Operation needs a running session |
MCS_ERR_NO_ACTIVE_SESSION |
-20 |
No session to read from |
MCS_ERR_SUBPROCESS_SPAWN_FAILED |
-8 |
The engine process failed to start |
MCS_ERR_HANDLE_INVALID |
-9 |
NULL or invalid handle |
MCS_ERR_TIMEOUT |
-10 |
Wait or graceful-stop time elapsed |
MCS_ERR_STATUS_FILE_MISSING |
-11 |
status.json not written yet |
MCS_ERR_SESSION_ABORTED |
-12 |
Session ended abnormally |
MCS_ERR_LOCATIONS_UNAVAILABLE |
-21 |
Engine paths not yet resolved |
MCS_ERR_INVALID_PARAMETER |
-14 |
A bad argument was passed |
MCS_ERR_IO |
-13 |
An I/O operation failed |
MCS_ERR_INTERNAL |
-15 |
Unexpected internal error |
In Python the same codes are available as mcs.ERR_* (again with the MCS_
prefix dropped, so MCS_ERR_TIMEOUT is mcs.ERR_TIMEOUT) and appear as the
integer .code on a raised MCSError.
Support
- Developer and integration help, and bug reports: support (at) osinix.com
- Licensing, SDK access, and add-ons: licensing (at) osinix.com
- Documentation: https://osinix.com/docs
© Osinix.
