Transports
MCP
The API as tools for an assistant, over the Model Context Protocol.
The gateway serves https://api.inorbit.hr/mcp (streamable HTTP, stateless, MCP
2025-11-25): a fixed allowlist of operations as tools, named <backend>_<method> and
described by the contract's own comments. The caller is whoever the token names, as on
every other surface: the same rights, the same budget, the same record.
Scopes
A token reaches /mcp when it holds an MCP scope, and each tool needs its own:
| Scope | Tools |
|---|---|
mcp:read | the tools that only read: the models and your budget, the sandbox's languages, the EVM lab's reads |
mcp:generate | the tools that run a model: llm_generate, llm_embed, counted against your budget |
mcp:write | the tools that change something: connection actions a grant gives the token (with connections:use), drafts, comments and reviews in your spaces (with decisions:write) |
decisions:read | beside mcp:read, Decisions' reads: your spaces, their documents, versions, comments, reviews and diagrams |
decisions:write | beside mcp:write, Decisions' writes (see Decisions) |
tools/list lists only the tools the token may call. A call to a tool whose scope it
lacks is 403 with WWW-Authenticate: Bearer error="insufficient_scope", scope="..."
naming what the tool needs. A full API token (iohr.api) meets every scope.
Every tool says what it does through MCP's annotations (readOnlyHint,
destructiveHint, idempotentHint, openWorldHint), so a client can ask you before it
runs one that changes something.
Connecting an assistant
Give the assistant the address https://api.inorbit.hr/mcp and nothing else. It finds
our sign-in, registers itself, and sends you to a page that names the app, where your
browser goes back to afterwards, and what it may do in plain words. Allow it there; it
then acts as you, under the scopes you allowed, until you revoke it under
Sign-in and security → Connected apps in the console. The page does not ask again for
30 days. The app's access token lasts 15 minutes and is renewed with a refresh token that
changes on every use.
- claude.ai (and Claude Desktop): Customize → Connectors → Add custom connector, the address above, then Connect.
- ChatGPT: Settings → Apps & Connectors → Advanced settings → turn on Developer mode, then Create, the address above, OAuth as the authentication.
- Claude Code:
claude mcp add --transport http inorbit https://api.inorbit.hr/mcp, then/mcpin a session and choose to authenticate. - Cursor: in
~/.cursor/mcp.json,{"mcpServers": {"inorbit": {"url": "https://api.inorbit.hr/mcp"}}}, then Connect in Settings → MCP. - VS Code: in
.vscode/mcp.json,{"servers": {"inorbit": {"type": "http", "url": "https://api.inorbit.hr/mcp"}}}, then Start on the server. - Gemini CLI:
gemini mcp add --transport http inorbit https://api.inorbit.hr/mcp, then/mcp auth inorbit.
An app that registers itself gets a public client (no secret, PKCE with S256) that may
send you back to an https address or to a loopback address on your own computer, and
nothing else; its tokens open /mcp and no other route. A client that only returns to
your own computer (a loopback address) gets a warning on the page: any program on your computer could
claim to be it, so allow it only right after you started the connection yourself.
Registration is open to anyone and limited per address; a client nobody signs in with
within 30 days is removed.
With an API token instead
Make an API token in the console with mcp:read (and
mcp:generate if the assistant may run the models; decisions:read, or mcp:write and
decisions:write as well, for Decisions), then:
claude mcp add --transport http inorbit https://api.inorbit.hr/mcp \
--header "Authorization: Bearer $IOHR_TOKEN"Or, for everyone working in a repository, in its .mcp.json with the token taken from the
environment:
{
"mcpServers": {
"inorbit": {
"type": "http",
"url": "https://api.inorbit.hr/mcp",
"headers": { "Authorization": "Bearer ${INORBIT_TOKEN}" }
}
}
}Other assistants
Any client that sends a bearer header works the same way, with a token that holds the MCP scopes it needs:
// ~/.cursor/mcp.json, or .cursor/mcp.json in a project
{
"mcpServers": {
"inorbit": {
"url": "https://api.inorbit.hr/mcp",
"headers": { "Authorization": "Bearer ${env:INORBIT_TOKEN}" },
},
},
}Who you are
accounts_get_me (title "Who am I", with account:read) answers who is calling: the
person or API key the gateway verified, their own account, the team accounts they belong
to, and for a key the scopes it carries. Call it first to learn which account_id the
other tools take. Your model budget left today is llm_get_budget, and an account's
spaces are decisions_list_spaces. It reads your own row only, never anyone else's.
Monitors
With connections:read, an assistant reads your monitors as evidence, under your rights
in each account, and changes nothing:
connections_list_monitors: the account's monitors with their state (up, down, unknown, paused), narrowed by connection, agent, category or tags.connections_get_monitor: one monitor, what it checks, from where and how often.connections_list_monitor_runs: its check runs, newest first: when, from where, pass or fail, the latency and the kind of error. Never a response body.connections_get_monitor_summary: uptime over 24 hours, 7 and 30 days, latency percentiles and the last failure.connections_get_monitor_series: a time range in buckets, with the failures by kind.
Creating, changing, pausing and deleting a monitor stay in the console and the API.
Decisions
With decisions:read, an assistant works with your spaces as you do in Decisions,
under your rights in each account:
- Read.
decisions_list_spaces(your spaces;space_idis a space's id),decisions_get_space,decisions_list_documents(withcompact: true, each document as only its id, kind, number, title, status and access, to find one by),decisions_get_document(the text of a version and its findings;document_idtakes the id, or what people call the document:RFC 0102.1,prd-12,adr 55,study 13, or its repository pathdocs/rfcs/0102.1-slug.md),decisions_list_versions,decisions_list_timeline,decisions_list_comments,decisions_list_reviews,decisions_list_review_queue(reviews asked of you),decisions_get_waiting(everything waiting on you across an account's spaces),decisions_list_questions,decisions_get_settings(a space's approval rules, including what assistants may approve),decisions_list_diagramsanddecisions_get_diagram. - Write, with
mcp:writeanddecisions:write.decisions_create_documentstarts a PRD, an ADR, an RFC or a study,decisions_save_documentsaves a draft as a new version,decisions_add_commentanddecisions_resolve_commenttake part in a thread,decisions_request_reviewasks people to review,decisions_submit_reviewasks for changes,decisions_set_statusmoves a document along its statuses,decisions_ask_questionasks a person to settle something, anddecisions_create_diagramanddecisions_save_diagramdraw. None of them removes anything, and each says so in its annotations. - Saving. A save names the
base_versionit was made from. If someone saved since, the tool answers an error withcode: "conflict"and the current version's number; nothing is overwritten and nothing is retried. The assistant reads the document again, applies its change there and saves on that version. - Left to you. Approving a version, deciding or publishing a document, the spaces
themselves and their settings, deleting a diagram and the export stay in the console.
decisions_submit_reviewtakesdecision: "changes"only; an approval is refused before it reaches the service. - Names. These are the only names: the
rfcs_*andlabs_*tools and therfc:*andlab:*scopes were replaced on 2026-10-08 (see the changelog).
A document is also a resource, inorbit://spaces/{space}/documents/{document}, read
as Markdown with resources/read under the same scopes as decisions_get_document.
resources/templates/list names the template; resources/list is empty, because
documents are found with the tools.
Every tool
MCP tools lists each tool with its scopes, its arguments and
an example call. It is generated from the server's own list on every build, so it never
falls behind tools/list.
Discovery
/mcp is an OAuth protected resource. A request without a valid token is 401 with:
WWW-Authenticate: Bearer scope="mcp:read", resource_metadata="https://api.inorbit.hr/.well-known/oauth-protected-resource/mcp"The metadata (RFC 9728) is public, at
/.well-known/oauth-protected-resource/mcp and at the root address:
{
"resource": "https://api.inorbit.hr/mcp",
"authorization_servers": ["https://auth.inorbit.hr"],
"scopes_supported": [
"mcp:read",
"mcp:write",
"mcp:generate",
"decisions:write",
"decisions:read",
"connections:use"
],
"bearer_methods_supported": ["header"],
"resource_name": "InOrbit",
"resource_documentation": "https://developers.inorbit.hr/docs/transports/mcp"
}A token is accepted when its audience is the API's (iohr-api) or this server's
(https://api.inorbit.hr/mcp). A token minted for this server opens /mcp and no other
route.
The sign-in's metadata is at https://auth.inorbit.hr/.well-known/oauth-authorization-server
(RFC 8414). Besides the sign-in's own endpoints
it names the registration endpoint (https://auth.inorbit.hr/oauth2/register,
RFC 7591), S256 as the PKCE method and none
among the token endpoint's authentication methods.