# Make your first calls

> Send initialize, list the tools and call one with curl, and read what comes back, including the structured result and the error shapes.

Source: https://zorlya.com/guides/developers/make-your-first-calls

Updated 2026-10-25.

With an access token from [Sign in with OAuth by hand](/guides/developers/sign-in-with-oauth-by-hand), send each JSON-RPC 2.0 message as a `POST` to `https://mcp.zorlya.com`. This guide sends `initialize`, lists the tools, calls `top_matches`, reads a resource and renders a prompt with curl.

Set two variables:

```bash
TOKEN=paste-your-access-token
MCP=https://mcp.zorlya.com
```

## 1. Say hello

`initialize` starts the exchange. You send the protocol version you speak, and the server answers with the version it will use:

```bash
curl -s $MCP \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-client","version":"0.1.0"}}}'
```

The result carries `protocolVersion`, `capabilities`, `serverInfo` (`name` is `zorlya`) and `instructions`, a few paragraphs written for an AI model that explain how to read scores and how to page. The server accepts `2026-07-28`, `2025-06-18`, `2025-03-26`, `2024-11-05` and `2024-10-07`. If you ask `initialize` for a version it does not know, it answers with `2025-06-18`.

From the `2026-07-28` revision, a request must say its method in an `Mcp-Method` header (and, for `tools/call`, `resources/read` and `prompts/get`, the tool, resource or prompt in `Mcp-Name`). Send `MCP-Protocol-Version: 2026-07-28` with them. A header that is missing or disagrees with the body is refused with the JSON-RPC error `-32020`, and a version the server does not speak with `-32022`, which lists the versions it does. The examples below omit the headers and so speak the older revision, which needs none. A request from a browser page must come from one of Zorlya's own app origins, or the server answers `403`.

Real clients then send a notification that they are ready. A notification has no `id`, and the server answers it with an empty `202`:

```bash
curl -s -o /dev/null -w "%{http_code}\n" $MCP \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
```

The server keeps no session, so this step changes nothing on its side. You can call `tools/list` without it. Send it anyway if you want your client to behave like the others.

## 2. List the tools

```bash
curl -s $MCP -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
```

What you get depends on the account the person chose. For a job seeker with read access you get 15 tools, and with write access as well, 20. A hiring account lists up to 11, a support account up to 7 and a staff account up to 17, each filtered by the scopes granted and the person's role. Each tool has a `name`, a `description` and an `inputSchema` in JSON Schema. Read the schema instead of guessing arguments: for example, `get_job` requires `job_id`, and `search_jobs` takes `query`, `location`, `remote_only`, `min_score`, `limit` and `cursor`.

## 3. Call a tool

```bash
curl -s $MCP -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"top_matches","arguments":{"limit":2}}}'
```

The result has a `content` array. Its first item is `{"type":"text","text":"…"}`, and the text is a JSON document you parse yourself. A trimmed example:

```json
{
  "count": 2,
  "has_more": true,
  "next_cursor": "anAxOjI=",
  "matches": [
    {
      "job_id": "5a3b1e00-0009-4000-8000-00000000001f",
      "title": "LLM Application Engineer",
      "company": "Granite Peak",
      "remote": true,
      "score": 92.6,
      "sub_scores": { "semantic": 98.0, "skills": 98.0, "salary": 70.0, "geo": 100.0 },
      "explanation": "Your work with FastAPI lines up with the core of this role. Salary band not stated."
    }
  ]
}
```

The same data also comes back as `structuredContent` next to `content`, already parsed, so a client that reads it can skip the text. `has_more` and `next_cursor` tell you there is another page. [Page results and handle errors](/guides/developers/page-results-and-handle-errors) shows how to follow it.

## 4. Read a resource and a prompt

A resource is read by address:

```bash
curl -s $MCP -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":4,"method":"resources/read","params":{"uri":"zorlya://tracks"}}'
```

The result is `contents`, each with `uri`, `mimeType` (Markdown) and `text`. `resources/list` shows the four fixed resources, and `resources/templates/list` shows `zorlya://job/{job_id}`.

A prompt is rendered with its arguments:

```bash
curl -s $MCP -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":5,"method":"prompts/get","params":{"name":"find_roles","arguments":{"keywords":"python","remote_only":"true"}}}'
```

The result is `messages` ready to hand to a model. `assess_fit` needs `job_id` and the others take optional arguments.

## Other methods

`ping` answers `{}`. `tools/list`, `resources/list`, `resources/templates/list` and `prompts/list` take no arguments. Anything else answers a JSON-RPC error with code `-32601` (HTTP 404 from the `2026-07-28` revision).

## What we checked

The requests in this guide were run against a local copy of the server on 2026-10-17, and the example output is trimmed from those runs. On 2026-10-18 the address and the header rules were updated from the server's source and not run again. Nothing was run against the production server.
