What Is the Purpose of the `services` Package in holaOS?
The services package in holaOS serves as the core abstraction layer that exposes runtime-level APIs to the desktop app, plugins, external integrations, and Python backend, wrapping MCP/oRPC infrastructure to provide type-safe, identity-aware RPC calls.
In holaboss-ai/holaOS, the services package sits at the center of the architecture, bridging higher-level code with the underlying Docker-compose based runtime. It ensures that every component—from the React-based desktop UI to third-party plugins—interacts with workspace operations, memory handling, and app orchestration through a consistent, generated interface.
The Role of the services Package in holaOS Architecture
The holaOS runtime consists of multiple moving parts: Docker containers for isolated workspaces, a Python backend for AI workloads, and a TypeScript-based API server. The services package unifies access to these components through a single, well-defined contract.
Core Responsibilities
The package fulfills four critical functions:
- MCP/oRPC Infrastructure Wrapping — Each request receives a "service context" (runtime services plus optional identity) created by a factory resolved per-tool call.
- Type-Safe Client Generation — Built from the OpenAPI contract, it eliminates hand-crafted HTTP requests and prevents type drift.
- Gateway Abstraction — Higher-level code never talks directly to Docker containers or Python services; all traffic routes through
services. - Contract Synchronization — Generated via Kubb, the package auto-updates when the server API changes, keeping client and server in lockstep.
How the Service Context Works
In packages/remote-api/src/mcp/index.ts, the service context is defined and instantiated for every tool invocation:
// packages/remote-api/src/mcp/index.ts
// Defines per-request service context: services + optional identity
interface ServiceContext {
services: RuntimeServices;
identity?: Identity;
}
// Factory resolved per tool call
async function resolveServiceContext(request: MCPRequest): Promise<ServiceContext> {
const identity = await authenticate(request);
const services = await createRuntimeServices();
return { services, identity };
}
This pattern ensures that identity propagation and resource isolation happen automatically—every call carries the user's permissions and operates within the correct workspace boundary.
The API server wires these services into the HTTP layer in runtime/api-server/src/app.ts:
// runtime/api-server/src/app.ts
// Wires MCP services into HTTP layer
app.use('/mcp', createMCPRouter({
// contract, so the oRPC/MCP services resolve the single workspace server-side
resolveContext: resolveServiceContext
}));
Key Files and Their Functions
| File Path | Purpose |
|---|---|
packages/remote-api/src/mcp/index.ts |
Defines service context factory and per-request resolution logic |
runtime/api-server/src/app.ts |
Central server integrating MCP services with HTTP routing |
packages/app-sdk/README.md |
Documents the generated SDK built atop services |
runtime/api-server/src/interaction-memory.ts |
Consumes services to fetch/store workspace interaction memory |
Practical Usage Examples
Fetching Workspace Memory via SDK
The generated client abstracts the underlying services call:
import { useMemoryQuery } from '@/app-sdk';
// React component
function MemoryPanel({ workspaceId }: { workspaceId: string }) {
const { data, isLoading } = useMemoryQuery({ workspaceId });
if (isLoading) return <Spinner />;
return <MemoryDisplay data={data} />;
}
Under the hood: useMemoryQuery → generated client → services memory service → MCP layer → Docker runtime.
Creating a Workspace from a Plugin
import { workspaceService } from '@/services';
async function createWorkspace(name: string): Promise<string> {
// Service context (identity + runtime services) injected automatically
const workspace = await workspaceService.create({ name });
return workspace.id;
}
The MCP layer handles Docker-compose orchestration, network isolation, and state persistence transparently.
Running Background Jobs
import { backgroundService } from '@/services';
await backgroundService.runJob({
workspaceId: 'prod-123',
jobName: 'sync-contacts',
config: { retryPolicy: 'exponential-backoff' }
});
The services abstraction routes this to the appropriate container, streams logs, and updates workspace state without exposing Docker internals.
Integration with the Broader Ecosystem
Desktop App
The React-based desktop UI consumes services through auto-generated React-Query hooks, ensuring caching, deduplication, and optimistic updates without manual implementation.
Plugin SDK
Third-party plugins receive a restricted services client pre-configured with their declared permissions. This prevents unauthorized workspace access while maintaining the same ergonomic API.
Python Backend
The Python services expose gRPC endpoints that the services package wraps, allowing TypeScript callers to interact with AI workloads as if they were local functions.
Summary
- The
servicespackage is the single source of truth for runtime API access in holaOS. - It wraps MCP/oRPC to provide automatic identity injection and per-request context isolation.
- Generated from the OpenAPI contract, it guarantees type safety and prevents client/server drift.
- All higher-level code—desktop UI, plugins, integrations—routes through this layer rather than directly touching Docker or Python services.
Frequently Asked Questions
What protocol does the services package use under the hood?
The package uses MCP (Model Context Protocol) over oRPC, a TypeScript-first RPC framework. This combination provides streaming, type-safe communication between the API server and runtime services. As implemented in packages/remote-api/src/mcp/index.ts, each request establishes a fresh service context through the MCP resolver.
How does the services package handle authentication?
Authentication happens at the context resolution layer. When resolveServiceContext is called, it extracts credentials from the incoming request, validates them against the identity provider, and attaches the resulting Identity object to the context. All downstream service methods receive this identity and enforce permissions accordingly.
Can I use the services package outside the holaOS codebase?
Yes—the generated client in packages/app-sdk is designed for external consumption. Install the SDK, configure the API endpoint, and use the same type-safe methods as internal holaOS components. Note that external usage requires valid API credentials and appropriate workspace permissions set up in the holaOS control plane.
What happens when the server API changes?
The services package is regenerated automatically via Kubb whenever the OpenAPI spec updates. This process updates TypeScript types, function signatures, and React-Query hooks. CI pipelines in holaboss-ai/holaOS enforce that client code compiles against the latest generated types, catching breaking changes before deployment.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →