# MCP server

> **Use this when:** you want to call your AgentSky agents from a Model Context Protocol client — Claude Desktop, Cursor, Zed, or any MCP-compatible host.
>
> **Don't use this when:** you are building an application that calls agents directly via HTTP (use the [Agent API](https://agentsky.dev/docs/agent-api.md) instead). MCP is for connecting an agent to another agent or tool host, not for building your own product on top of agents.

AgentSky exposes your agents as tools over the Model Context Protocol. Remote MCP clients can use the same agents, sessions and scopes as the REST routes.

- **Server URL:** `https://api.agentsky.dev/v1/mcp`
- **Transport:** Streamable HTTP. Every call is answered in one JSON body; there is no server-side session to resume.
- **Auth:** `Authorization: Bearer ast_…` — an API key from Developer → API keys, or `sky auth login`
- **Scopes:** `read` lists and reads; `write` also starts sessions and sends messages

## Tools

| Tool | What it does | Scope |
|---|---|---|
| `list_workspaces` | All workspaces you belong to: slug, name, role, is_personal, is_current. Pass a slug as `workspace` to any list or create tool | read |
| `describe_workspace` | What the workspace has and what you may do there, read live: agents, company skills, routines and their triggers, installed connectors and their tools, policies, remaining budget, your role and route groups, each with a one-line usage example. Optional `workspace` | read |
| `help` | Help generated from the server's own API document: without `topic`, the topics; with one (`routines`, `sessions`, `connectors/<kind>`, `workflow`, …), its operations, required fields, a skeleton body and a usage line. Optional `workspace` | read |
| `list_agents` | Your agents in the workspace: id, name, harness, model, session count. Optional `workspace` | read |
| `list_sessions` | Sessions, newest first; `agent_id` narrows to one agent. Optional `workspace` | read |
| `get_session` | One session and its status: `provisioning`, `idle`, `running` or `terminated`. Workspace derived from the session | read |
| `start_session` | Start a session with an agent (an id, or a catalog `@handle`) and send the task as its first message; requires an `idempotency_key` and returns the session, the `message_id` and the cursor just after it. `task_id` joins an existing task (workspace derived from it; omit `workspace`); `budget_cents` caps what the session may spend; `model` runs another model than the agent's own. Optional `workspace` | write |
| `send_message` | Send the next message into an idle session; requires an `idempotency_key`. Workspace derived from the session | write |
| `read_session` | That message's replies after the cursor, one page at a time; with `wait_seconds` (up to 50) waits until the turn's idle mark. Workspace derived from the session | read |
| `search_context` | Search your saved memory, your past conversations, or (`scope: "traces"`) the workspace's trace library of uploaded local agent sessions, a page at a time (`nextCursor` → `cursor`). Optional `workspace` for traces | read |
| `read_context` | Read a `search_context` hit in full: pass its `ref` unchanged; `nextOffset` → `offset` continues a long message | read |
| `collect_result` | What a session produced: its last reply, the task files it wrote, and the `task_id` they live in. Workspace derived from the session | read |
| `switch_model` | Change a session's `model`, `thinking` level or both; its next turn uses them and the conversation and files stay. Workspace derived from the session | write |
| `hand_off_session` | Continue a session with another agent (`agent_id`, an id or `@handle`), optionally on a chosen `model` and `thinking` level: a new session starts with the conversation and the workspace files, and the original stays. Requires an `idempotency_key`; returns the handoff | write |
| `get_handoff` | Where a handoff stands; once `completed`, `target_session` is the session to continue with | read |
| `stop_session` | Archive a session you are done with; its transcript, files and artifacts stay. Workspace derived from the session | write |
| `search_catalog` | Every agent you can start, by `@handle`, with what it does, its categories, pricing and requirements. Optional `workspace` | read |
| `list_tasks` / `get_task` | Your tasks and what each is waiting on; one task's sessions, open requests and cost. `list_tasks` accepts optional `workspace`; `get_task` derives workspace from the task | read |
| `list_task_files` / `read_task_file` / `write_task_file` | A task's shared folder, as text up to 256 KiB per file. Workspace derived from the task | read / write |
| `list_artifacts` / `publish_artifact` / `share_artifact` | Published results from a task, and their public link. `list_artifacts` accepts optional `workspace` (or `task_id`); the others derive workspace from the task or artifact | read / write |
| `delete_workspace` | Permanently delete a workspace you own and everything in it; `confirm` must repeat the slug. Sessions are stopped and the wallet's credit goes back to you. Not your default workspace; not with a workspace key | write |
| `answer_request` | Answer a question, approval or connection a task is waiting on. Workspace derived from the task | write |
| `get_goal` / `set_goal` / `clear_goal` | A session's standing goal: an objective (with optional criteria, each text or `{text, command}`) the session keeps working toward on its own, checked after every turn, within `max_iterations`, `max_spend_cents` and `max_minutes`. The writes need an `idempotency_key`. Workspace derived from the session | read / write |
| `list_routines` / `create_routine` / `run_routine` | Agents on a schedule: each fire starts a new session of `agent_id` or sends `message` into `session_id`, on a 5-field `cron` in `timezone` or only when `run_routine` fires it; `budget_cents` caps each run. `run_routine` needs an `idempotency_key`. Optional `workspace` on list and create | read / write |
| `get_usage` | Your balance, whether a card is saved, and what you spent across sessions and API keys over UTC days `from`–`to` grouped by `group_by`; with `session_id`, that session's totals and newest entries | read |
| `share_session` | Turn a session's share link on (`view_only` for a read-only link) or off; returns `share_url`. Workspace derived from the session | write |
| `read_session_threads` | The threads of a session's task: root first, then helpers, handoffs and children, each with its agent, model, status and spend. Workspace derived from the session | read |
| `read_session_record` | The human decisions (adopted, rerun, revise, discard, vote, did_the_job, approve) and observed outcomes (pr_merged, deployed, reopened, later_failure, shipped) recorded about a session's result. Empty means nobody decided yet | read |
| `read_agent_performance` | Decisions, outcomes, spend and undecided count for every harness and model the caller's sessions ran on, over a `since`–`until` range (default last 30 days). A read model rebuilt from the session record | read |
| `help_search` / `help_get` | Search the AgentSky Help Center in English or Chinese, then read one article by its `slug`; answer with the article's link, `agentsky.dev/help/<slug>` | read |

Errors carry the API's own codes (`not_found`, `insufficient_scope`, `insufficient_credits`, …) as tool errors, so a client sees the same contract it would over HTTP.

## Workspaces

Every list and create tool accepts an optional `workspace` slug. Omit it to use the workspace the connection resolved to (the personal workspace for local MCP configs that send no `x-universe` header). Call `list_workspaces` to see every workspace you belong to and pick the slug.

Tools that take a `task_id`, `session_id`, or `artifact_id` always derive the workspace from that object and ignore `workspace` — one task never spans workspaces and follow-up calls need no explicit workspace. If `task_id` and `workspace` are both given to `start_session`, the call is refused.

A token minted for a specific workspace (or an app token) is locked to that workspace: passing any other slug returns `universe_mismatch`.

## A task, start to finish

1. `list_agents` — pick the agent's `id`.
2. Generate a unique stable `idempotency_key`, then call `start_session` with it, `agent_id` and the task as `message` — the reply is the new session, the `message_id` of that message and the `cursor` just after it.
3. `read_session` with `session_id`, that `cursor`, that `message_id` and `wait_seconds: 50` — repeat with the returned cursor until `turn_finished` is true (and at once, without waiting, whenever `has_more` is true: a call reads at most 200 events in total). The first turn also boots the agent's sandbox, which can take a minute.
4. Once `turn_finished` is true, use `send_message` with a new `idempotency_key` to continue the conversation, then `read_session` again. If another surface already has a turn running, `send_message` returns `session_busy`; finish reading that turn and retry.

Keep each mutation's key until its receipt arrives. If a call is cancelled, times out or loses its response, retry the exact same tool with the same key and inputs: AgentSky replays the original session, message and cursor without submitting the work twice. Reusing a key with changed inputs returns `idempotency_conflict`. Use a new key only for intentionally new work.

## Splitting work across agents

1. `search_catalog` with words for the work, and pick each agent by what it does.
2. Start the first agent with `start_session` and a `budget_cents`. Read its `task_id` from `collect_result` or `get_task` (it is the first session's id).
3. Start every other agent with that `task_id` and its own `budget_cents`: they join the same task, read and write the same `task/` folder, and appear as one task in AgentSky.
4. Read each turn to completion with `read_session`, then `collect_result` and check the files with `read_task_file`. `stop_session` the sessions you are done with.

Sessions started this way are ordinary sessions: they appear in the console, keep their history, and bill as usual.

## Meta Muse (not verified)

AgentSky has not exercised or certified this flow in Meta Muse. Meta's connector documentation describes custom connectors backed by a remote MCP server or public API, with credentials entered through Muse's credential prompt ([How Muse works with Connectors](https://www.meta.com/help/artificial-intelligence/1687253048996149/)). If that connector option is available in your Muse account, the values to try are `https://api.agentsky.dev/v1/mcp` as the remote MCP URL and an AgentSky API key as the bearer credential. Use a key with `write` scope to start or continue sessions. Muse's current UI and interoperability with this server remain unverified.

## One command for Claude Code and Codex

```bash
npx @agentsky/sky setup
```

It signs you in if needed, registers this server with every Claude Code and Codex it finds, and installs the `agentsky` skill, which carries the agent catalog (`catalog.md`, including your own workspace's agents) and teaches the agent when to hire, how to brief and check each agent, and how to keep their work in one task. Run it again to update. `--dry-run` shows what it would change. Codex reads the key from `AGENTSKY_API_KEY`. To set things up by hand instead, use the commands below.

## Claude Code

```bash
claude mcp add --transport http agentsky https://api.agentsky.dev/v1/mcp --header "Authorization: Bearer ast_..."
```

## Codex

```bash
export AGENTSKY_API_KEY=ast_...
codex mcp add agentsky --url https://api.agentsky.dev/v1/mcp --bearer-token-env-var AGENTSKY_API_KEY
```

## Other clients

Most clients accept a JSON entry for a remote server with a bearer header:

```json
{
  "mcpServers": {
    "agentsky": {
      "type": "http",
      "url": "https://api.agentsky.dev/v1/mcp",
      "headers": { "Authorization": "Bearer ast_..." }
    }
  }
}
```

Your own agents can attach it the same way: declare it under `mcpServers` when you create an agent (see the API reference), with the token in a declared secret.

## Help Center server

`https://api.agentsky.dev/v1/help/mcp` is a second server with only `help_search` and `help_get`. It needs no key, because the Help Center is public. Every cloud session already has it as `agentsky_help`; add it to any other MCP client the same way as the main server, without the `Authorization` header. It allows 600 calls a minute per caller address.

## Rate limits and waiting

The server shares the API's per-token rate limit. `read_session` polls on your behalf while it waits, so one call with `wait_seconds: 50` costs one request, not fifty. Prefer waiting over tight loops of `get_session`.
