TypeScript-first SDKs for building AI agent applications where the agent loop runs on your server, clients execute local tools, and every run can pause, resume, and be inspected through a stable protocol.
Mido is designed for products that need more control than a single hosted agent call can provide:
- Server-owned agent loops with provider-neutral model adapters.
- Resumable client-side tools for browser, desktop, mobile, or native capabilities.
- Human-in-the-loop approval for interactive and destructive actions.
- MCP integration on either side of the server/client boundary.
- AG-UI adapters, JSON Schemas, and conformance helpers for stable client contracts.
- Durable checkpoints, thread/event stores, tracing, and local run inspection.
Use Mido when your app needs the model to reason on the server while the client keeps ownership of local context, user approvals, credentials, or device-only capabilities.
@mido-agent/protocol-coreShared event types, run request types, tool contracts, JSON Schemas, and schema validation helpers.@mido-agent/protocol-aguiBoundary adapter betweenCoreEventand AG-UI-shaped events.@mido-agent/mcp-coreManaged MCP connections, health checks, tool refresh diffs, and mapping helpers for server or client runtimes.@mido-agent/server-sdkServer-owned agent loop, tool routing, suspend/resume flow,SessionStore, durable thread/event stores, tracing, user memory system with autonomous write, and provider adapters including DeepSeek.@mido-agent/client-coreTransport-agnostic client runtime with local tool registry, pending interactive tool state, and automatic resume forclient_autotools.@mido-agent/client-webBrowser transport, React hooks, and a minimal reference panel.MidoClientiOS Swift 6 client SDK packaged with Swift Package Manager underpackages/client-ios.@mido-agent/toolkit-coreOptional agent tools for workspace access, web search/fetch, document retrieval, browser automation adapters, and scoped memory.@mido-agent/conformanceJSON Schema export, native client contract docs, and round-trip conformance helpers.@mido-agent/evaluatorOffline run metrics, suite aggregation, reproducible run artifacts, deterministic graders, and local smoke/safety reports.
.
├── docs/
│ ├── README.md
│ ├── roadmap.md
│ ├── architecture.md
│ ├── data-flow.md
│ ├── storage-and-tracing.md
│ ├── agent-skills.md
│ ├── plans/
│ └── archive/
├── packages/
│ ├── client-core/
│ ├── client-ios/
│ ├── client-web/
│ ├── conformance/
│ ├── evaluator/
│ ├── mcp-core/
│ ├── protocol-agui/
│ ├── protocol-core/
│ ├── server-sdk/
│ └── toolkit-core/
└── tests/
Use the same Mido SDK version across server and client packages unless the compatibility matrix says otherwise.
pnpm add @mido-agent/server-sdk@0.1.0 @mido-agent/client-core@0.1.0
pnpm add @mido-agent/client-web@0.1.0For Swift Package Manager, depend on a repository tag:
.package(url: "https://github.com/kingiol/Mido.git", from: "0.1.0")Use main only for local development or testing unreleased changes.
| Mido SDK | Protocol | Server SDK | Web Client | iOS Client |
|---|---|---|---|---|
0.1.x |
mido.protocol.v1 |
0.1.x |
0.1.x |
0.1.x |
SDK publishing is tag-driven through GitHub Actions. Keep the source version in sync first:
pnpm version:set 0.2.0
pnpm release:check
git commit -am "chore: release SDK 0.2.0"
git push origin mainThen push one tag for the SDK you want to publish:
| Tag | Publishes |
|---|---|
protocol-core-v0.2.0 |
@mido-agent/protocol-core to npm |
protocol-agui-v0.2.0 |
@mido-agent/protocol-agui to npm |
mcp-core-v0.2.0 |
@mido-agent/mcp-core to npm |
server-sdk-v0.2.0 |
@mido-agent/server-sdk to npm |
client-core-v0.2.0 |
@mido-agent/client-core to npm |
client-web-v0.2.0 |
@mido-agent/client-web to npm |
toolkit-core-v0.2.0 |
@mido-agent/toolkit-core to npm |
conformance-v0.2.0 |
@mido-agent/conformance to npm |
evaluator-v0.2.0 |
@mido-agent/evaluator to npm |
v0.2.0 |
MidoClient Swift Package GitHub release |
Example:
git tag server-sdk-v0.2.0
git push origin server-sdk-v0.2.0npm publishing requires the repository NPM_TOKEN secret unless npm trusted publishing is configured. The publish workflow uses npm provenance and creates a GitHub release for the pushed tag. The iOS SDK intentionally uses v<semver> tags because Swift Package Manager resolves package versions from SemVer-compatible Git tags.
- The agent loop always runs on the server.
- The client consumes streamed events and only executes local tools.
servertools execute immediately inside the server loop.client_autotools execute on the client and resume automatically.client_interactivetools surface pending actions to the UI; approval executes the local handler, while rejection resumes without executing it.- Client-owned prompt preferences can be set globally with
createAgentClient({ systemPrompt }), updated withclient.setSystemPrompt(...), or provided per run withsendMessage(text, { systemPrompt }). Static strings and context-aware provider functions are supported. They are sent with the run request but are not stored in client conversation memory. - MCP tools follow the same policy split: server MCP tools become
servertools, and client MCP tools becomeclient_autotools that are advertised throughRunStartRequest.clientTools. - Server-owned system prompts can be configured with
createAgentRunner({ systemPrompt })and updated withrunner.setSystemPrompt(...). Static strings and context-aware provider functions are supported. When set, client-providedsystemmessages are treated as untrusted supplemental preferences and wrapped under the server prompt instead of being passed through as peer instructions. - Server-side multi-agent orchestration is supported through
createAgentTool(...). A childAgentRunnercan be wrapped as a normalservertool so the root runner keeps ownership of the user-facing conversation while specialists use their own prompts, tools, policies, and stores. - Agent Skills are supported as instruction/resource packages. Mido indexes
SKILL.mdfrontmatter, progressively loads selected instructions, supportsreferences/andassets/, emits audit events, and can runscripts/only when an explicit sandbox is configured. - User Memory persists user facts, preferences, and episode summaries across sessions.
UserMemoryStorecontracts support deterministic text retrieval, content-hash deduplication, and scoped per-user isolation. An autonomous write pipeline extracts candidates from user statements and tool results, evaluates them against policy, and writes or supersedes memories automatically. Memory context is injected into the server system prompt at run start through the existing provider mechanism. - Tool policy is opt-in. Existing runners behave the same unless
createAgentRunner({ toolPolicy })is configured. Tools can add lightweightmetadata.policyhints such asrisk,effects, andscopes;createDefaultToolPolicy()hides and blocks destructive non-interactive tools while keepingclient_interactivetools available for the existing approval flow. - The internal protocol stays provider-neutral.
- AG-UI stays an adapter layer, not the internal source of truth.
- Durable storage is split into checkpoint storage, thread storage, and event storage so each deployment can choose its own backing store.
Policy metadata is passive by default:
runner.registerTool({
name: 'deleteDraft',
description: 'Delete a draft',
executionPolicy: 'server',
inputSchema,
resultSchema,
metadata: {
policy: {
risk: 'destructive',
effects: ['delete'],
scopes: ['draft:delete']
}
},
execute
});Enable the default policy only when the app is ready to enforce it:
const runner = createAgentRunner({
modelAdapter,
sessionStore,
toolPolicy: createDefaultToolPolicy()
});The default policy is intentionally quiet: tools without policy metadata are allowed in balanced mode, low-risk tools are allowed, and destructive tools should use client_interactive if they need user approval.
Use createAgentTool(...) when a specialist needs its own prompt, model, tools, or policy, but the root agent should keep ownership of the user-facing conversation:
const researchRunner = createAgentRunner({
modelAdapter: researchModel,
sessionStore,
systemPrompt: 'You are a research specialist. Return concise findings.'
});
const mainRunner = createAgentRunner({
modelAdapter: mainModel,
sessionStore,
systemPrompt: 'You are the supervisor. Delegate research tasks when useful.'
});
mainRunner.registerTool(createAgentTool({
agentId: 'research',
name: 'researchAgent',
description: 'Delegate focused research tasks and return concise findings.',
runner: researchRunner,
maxModelCalls: 3,
timeoutMs: 60_000
}));The child agent runs as a separate server run and returns a compact tool result with agentId, childRunId, status, output text, and counters.
Use createAgentWorkflowTool(...) when the root agent should decide how many agents to create and how they depend on each other:
mainRunner.registerTool(createAgentWorkflowTool({
name: 'runAgentWorkflow',
description: 'Create and coordinate multiple agents for complex tasks.',
templates: {
research: {
description: 'Read-only research specialist.',
createRunner: () => createAgentRunner({
modelAdapter: researchModel,
sessionStore,
systemPrompt: 'You are a research specialist.'
})
}
},
allowAdHocAgents: true,
createAdHocRunner: request => createAgentRunner({
modelAdapter: workerModel,
sessionStore,
systemPrompt: request.agent.systemPrompt
}),
limits: {
maxAgents: 5,
maxParallelAgents: 2,
maxModelCallsPerAgent: 4
}
}));The root model can call this one server tool with a DAG-shaped request: agents without dependsOn can run concurrently, while dependent agents wait for upstream results. Registered templates are preferred because the server controls their model, prompt, tools, and policy. Ad-hoc agents are allowed only when explicitly enabled and still run through the server-provided factory.
User Input
|
v
Server Agent Runner
|
+--> server tool --------> continue loop
|
+--> client tool --------> checkpoint + stream tool call
|
v
Client Runtime
|
+--> auto execute -----> POST resume
|
+--> user approve/reject -> POST resume
See Docs for the full documentation map and Roadmap for current priorities. See Architecture for the package boundaries and Data Flow for the annotated sequence diagrams. See Storage and Tracing for filesystem persistence, storage interfaces, and run inspector traces. See User Memory for the cross-session memory design, autonomous write pipeline, and integration architecture. See Evaluation for metrics, reproducible run artifacts, and no-key local smoke/safety evals.
Run local evaluator checks with:
pnpm eval:smoke
pnpm eval:storeMCP is an integration source, not a separate runtime class in Mido.
| MCP connection | Mido tool policy | Tool execution | Model visibility |
|---|---|---|---|
| Server-side MCP | server |
Server runner calls the remote MCP server | Registered directly on the server runner |
| Client-side MCP | client_auto |
Client runtime calls the remote MCP server | Sent with each RunStartRequest.clientTools |
Server-side MCP is useful when credentials and network access should stay in the server process. Client-side MCP is useful when the browser or native client owns the capability. In both cases the model only sees normal tool definitions.
Register no-script skills on the server:
const skillRegistry = await createAgentSkillRegistry({
rootDirs: ['./skills'],
maxLoadedSkills: 3,
maxPromptBytes: 48_000,
auditSink: event => console.log(event)
});
const runner = createAgentRunner({
modelAdapter,
sessionStore,
systemPrompt: 'Follow the application safety policy.',
skillRegistry
});Clients can send preferences without reading skill files:
await client.sendMessage('Please triage this ticket.', {
metadata: {
enabledSkills: ['support-triage']
}
});Native clients can keep local skill state with createAgentSkillManager({ store }) and pass it to createAgentClient({ skillManager }). The client will include enabled skill refs in metadata.skills.enabled for each run.
The iOS client SDK lives in packages/client-ios and is distributed as a Swift Package named MidoClient.
// Package.swift
.package(url: "https://github.com/kingiol/Mido.git", from: "0.1.0")For unreleased development builds, use branch: "main" only when you intentionally want the latest repository state instead of a tagged SDK release.
Then add the MidoClient product to your app target. The first iOS SDK pass includes Swift 6 Codable protocol models, an AgentClient actor, local client_auto and client_interactive tool handling, and a URLSessionSSETransport for the same SSE down + POST up contract used by the web client. The agent loop still runs on the server.
Verify the Swift package with:
swift test --package-path packages/client-iosTo enable scripts/, configure scriptSandbox and register createAgentSkillScriptTool(skillRegistry). See Agent Skills for the sandbox contract and safety controls.
MCP Streamable HTTP connections are wrapped with the managed connection helpers in the web demo. createManagedMcpHttpConnection exposes getStatus, subscribe, healthCheck, reconnect, close, and refreshTools. Tool calls retry once after a stale connection failure, and refresh helpers return added, updated, removed, and unchanged tool definitions so applications can update registration without duplicating tools.
Mido keeps provider behavior behind ModelAdapter. Adapters can also expose ModelAdapterCapabilities, so the runner can fail early when a run asks for capabilities the model does not support.
Available server SDK adapter entry points:
createDeepSeekModelAdapterfor DeepSeek native Chat Completions-style streaming.createVercelAiModelAdapterfor Vercel AI SDK stream normalization with caller-provided capabilities.createOpenAICompatibleModelAdapterfor OpenAI-compatible Chat Completions endpoints such as LiteLLM, OpenRouter, Ollama, vLLM, and LocalAI.createOpenAIResponsesModelAdapterfor OpenAI native Responses API behavior.
OpenAI-compatible defaults are intentionally conservative. Pass explicit capabilities for production provider checks because compatible endpoints vary in tool calling, usage, request id, and streaming behavior.
pnpm install
pnpm lint
pnpm test
pnpm run generate:schemas
pnpm buildRun the full local demo:
pnpm demoThat starts:
- API server on
http://localhost:3030 - Web client on
http://localhost:5173
Configure the DeepSeek provider before running the demo:
cp apps/web-demo/.env.example apps/web-demo/.envThen set:
DEEPSEEK_API_KEY=your_key
DEEPSEEK_MODEL=deepseek-v4-flash
DEEPSEEK_BASE_URL=https://api.deepseek.com
VITE_TENCENT_MAP_MCP_KEY=your_tencent_map_keyThe demo server reads env files in both locations:
apps/web-demo/.env.env
apps/web-demo/.env has higher priority and is the recommended place for demo-only keys.
Try these prompts in the demo UI:
weather in shanghaiweather heredelete draftsearch nearby coffee shops around Hangzhou West Lake
The web demo registers Tencent Map MCP as client-side MCP. It uses the Vite dev proxy at /mcp/tencent-map because the Tencent MCP endpoint does not allow direct browser CORS preflight.
Current provider notes:
- The web demo still uses DeepSeek by default.
- DeepSeek V4 flash mode declares tool resume support.
- DeepSeek V4 thinking mode is explicitly marked as not supporting Mido tool resume yet through adapter capabilities.
- Core packages compile and build.
- JSON Schemas are exported under
packages/conformance/schemas. - The test suite covers text-only runs, server tools, client auto tools, client interactive tools, AG-UI round trips, duplicate submission idempotency, and the browser SSE transport.
- Filesystem thread/event stores and
CoreEvent.tracesupport provide a local durable storage path for run inspection. - Provider adapter capabilities, preflight checks, OpenAI-compatible, and OpenAI Responses adapters are covered by focused tests.