https://loopin.works/mcp

MCP over streamable HTTP

Hire a human from your agent

Five tools. Two of them need no API key at all, because discovery should never require signing up. The other three spend or read money and need a key in the Authorization header.

endpointhttps://loopin.works/mcp

transport

streamable HTTP

protocol

2025-11-25

auth

Bearer API key

Connect

Copy one of these. Replace the key with the one from your dashboard — or leave the header out entirely and your agent can still search and read the catalogue.

Cursor

~/.cursor/mcp.json
Cursor
{
  "mcpServers": {
    "loopin": {
      "url": "https://loopin.works/mcp",
      "headers": {
        "Authorization": "Bearer lw_live_YOUR_KEY"
      }
    }
  }
}

Project-scoped config goes in .cursor/mcp.json instead.

Claude Code

one command, no file to edit
Claude Code
claude mcp add --transport http loopin https://loopin.works/mcp \
  --header "Authorization: Bearer lw_live_YOUR_KEY"

Drop the --header flag to connect for discovery only.

VS Code

.vscode/mcp.json
VS Code
{
  "servers": {
    "loopin": {
      "type": "http",
      "url": "https://loopin.works/mcp",
      "headers": {
        "Authorization": "Bearer lw_live_YOUR_KEY"
      }
    }
  }
}

Copilot Chat reads this file per workspace.

Anything else

It is plain JSON-RPC 2.0 over POST. Protocol versions accepted: 2025-11-252025-06-182025-03-262024-11-05

handshake
curl -s https://loopin.works/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"your-agent","version":"1.0.0"}}}'

The response carries an Mcp-Session-Id header. Send it back on later calls and we can attribute them to one session; omit it and everything still works.

The five tools

toolkeycostwhat it does
list_offeringsfreeSearch a catalogue of fixed-scope, fixed-price work delivered by a person.
get_offeringfreeFetch one offering in full: its exact scope, the inputs you must supply, its price in US cents, its turnaround in hours, and whether a slot is free right now.
book_offeringrequiredprice_centsHire the person.
get_bookingrequiredfreeCheck a booking.
get_balancerequiredfreeCheck the prepaid balance available for bookings, the per-booking spend cap the account owner set, and where a person can top the balance up.

list_offerings

open

Search a catalogue of fixed-scope, fixed-price work delivered by a person. Every result carries a firm price in US cents and a firm turnaround in hours, so you can decide in one step rather than waiting on a quote. Each result states exactly what is included, what is excluded, and what inputs you must supply. Free to call, no API key required, and nothing is booked or charged. Use max_price_cents to stay inside a budget before you commit.

inputSchema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "skill": {
      "description": "Optional skill filter. Matched against the fixed taxonomy: code-review, system-design, security-review, technical-writing, data-modelling, devops-review, api-design, prototyping. Near matches are resolved, so \"security audit\" finds security-review. A skill outside the taxonomy returns no results and is recorded, because it tells us what to add next.",
      "type": "string",
      "maxLength": 120
    },
    "max_price_cents": {
      "description": "Optional ceiling on price, in US cents. 20000 means $200.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 100000000
    },
    "max_turnaround_hours": {
      "description": "Optional ceiling on promised turnaround, in hours.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 8760
    },
    "limit": {
      "description": "Maximum results to return. Defaults to 10, capped at 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 25
    }
  }
}

get_offering

open

Fetch one offering in full: its exact scope, the inputs you must supply, its price in US cents, its turnaround in hours, and whether a slot is free right now. Free to call, no API key required, nothing is booked or charged. Worth calling before book_offering so the brief you write matches the scope.

inputSchema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "offering_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "The offering_id from list_offerings. The human-readable slug from the offering's url also works."
    }
  },
  "required": [
    "offering_id"
  ]
}

book_offering

API keyspends money

Hire the person. Books one offering, debits its fixed price from the account's prepaid balance, and returns a booking reference with the time delivery is promised by. Requires an API key. This spends money. The amount is exactly the offering's price_cents, taken from the balance, and it comes back in full if the work misses its promised time or the provider cancels. Confirm the price with get_offering or get_balance first if the budget is tight. Returns immediately — the work itself arrives later. Poll get_booking, or supply callback_url to be told. Write the brief to fit the offering's scope_included and supply everything in inputs_required; an out-of-scope brief is cancelled and refunded, which costs you the turnaround time.

inputSchema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "offering_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "The offering_id from list_offerings, or the offering's slug."
    },
    "brief": {
      "type": "string",
      "minLength": 20,
      "maxLength": 20000,
      "description": "What you need done, in plain prose. It must fit the offering's scope_included and stay clear of scope_excluded. A brief outside scope is cancelled and refunded in full, which costs you the turnaround time, so read the scope before writing this."
    },
    "inputs": {
      "description": "The material named in the offering's inputs_required: repository URLs, a diff, a schema, context. Supplying everything listed here is what lets the work start immediately instead of stalling on a question.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "callback_url": {
      "description": "Optional HTTPS endpoint. Receives a POST when the booking reaches a terminal state — delivered, cancelled, or refunded_late — carrying the same fields as get_booking. Polling get_booking works just as well if you have nowhere to receive a webhook.",
      "type": "string",
      "maxLength": 2000,
      "format": "uri"
    },
    "agent_name": {
      "description": "Optional name for the agent placing the booking. Shown to the person doing the work and used only for our own reach metrics.",
      "type": "string",
      "maxLength": 120
    }
  },
  "required": [
    "offering_id",
    "brief"
  ]
}

get_booking

API key

Check a booking. Returns its status, the time delivery is promised by, and the deliverable itself once it exists. Requires an API key. Free to call, so polling is fine. Status is 'booked' or 'in_progress' while the work is underway and 'delivered' once the deliverable is attached. 'refunded_late' means the promised time was missed and the money is already back on the balance — the work is still delivered when it lands, so keep polling. 'cancelled_by_provider' and 'cancelled_out_of_scope' are both refunded in full.

inputSchema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "booking_ref": {
      "type": "string",
      "minLength": 3,
      "maxLength": 40,
      "description": "The booking_ref returned by book_offering, for example \"LW-7K2M\"."
    }
  },
  "required": [
    "booking_ref"
  ]
}

get_balance

API key

Check the prepaid balance available for bookings, the per-booking spend cap the account owner set, and where a person can top the balance up. Requires an API key. Free to call. Worth calling before book_offering when you need to know whether a price is affordable: a booking above the cap or above the balance is refused rather than partially charged.

inputSchema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {}
}

Skills taxonomy

Small and fixed. Near matches resolve, so "security audit" finds technical-review and "take a photo" finds physical-verification. A skill outside the list returns nothing and is recorded — that record is how the catalogue grows.

physical-verificationlocal-errandphone-callhuman-feedbackcredentialed-acttechnical-reviewgeneral-task

A complete exchange

Discovery through to delivered work, using a real offering from the live catalogue. Responses are abridged where marked.

Photo verification — one location, GPS + timestamp · $45 · 2 days

1. Find work you can afford

No key needed. Every result carries a firm price and a firm turnaround, so the decision can be made in one step.

request
curl -s https://loopin.works/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_offerings","arguments":{"skill":"physical-verification","max_price_cents":20000}}}'
response
{
  "offerings": [
    {
      "offering_id": "7361a9c6-dc4d-4fe0-b9fa-698cb602a439",
      "slug": "photo-verification-one-location",
      "title": "Photo verification — one location, GPS + timestamp",
      "skill": "physical-verification",
      "summary": "A person goes to an address you name and photographs what is actually there.",
      "inputs_required": [
        "The full address, and a map pin if the address is ambiguous",
        "What specifically to photograph — the sign, the frontage, the entrance",
        "The question you want answered in one sentence",
        "Any time-of-day constraint, since opening hours may matter"
      ],
      "deliverable": "Photographs with GPS and timestamps, plus a short written answer to your question.",
      "price_cents": 4500,
      "turnaround_hours": 48,
      "available": true,
      "provider_name": "Operator"
    }
  ],
  "result_count": 1
}

2. Book it

key

This is the step that spends money. The price is debited from the prepaid balance and a booking reference comes back immediately, with the time delivery is promised by.

request
curl -s https://loopin.works/mcp \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer lw_live_YOUR_KEY' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"book_offering","arguments":{"offering_id":"photo-verification-one-location","brief":"Confirm the shop at this address exists and photograph the sign. The listing says 'Marlow Hardware' and I want to know whether that is what is actually written above the door.","inputs":{"address":"48 Fore Street, Bodmin PL31 2JB","photograph":"The frontage and the sign above the door","question":"Does the signage say Marlow Hardware?"},"callback_url":"https://your-app.example/hooks/loopin"}}}'
response
{
  "booking_ref": "LW-7K2M",
  "status": "booked",
  "price_cents": 4500,
  "due_at": "2026-07-26T09:14:03Z",
  "balance_after_cents": 15500,
  "deliverable": "Photographs with GPS and timestamps, plus a short written answer to your question."
}

3. Poll until the work arrives

key

Free to call. Status is 'booked' or 'in_progress' while a person is working, then 'delivered' with the deliverable attached. Supply a callback_url and you get told instead.

request
curl -s https://loopin.works/mcp \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer lw_live_YOUR_KEY' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_booking","arguments":{"booking_ref":"LW-7K2M"}}}'
response
{
  "booking_ref": "LW-7K2M",
  "status": "delivered",
  "due_at": "2026-07-26T09:14:03Z",
  "delivered_at": "2026-07-26T04:41:55Z",
  "deliverable": "Sign reads 'Marlow Hardware & Garden'. 4 photos attached, 50.4712,-4.7241, 2026-07-26T09:02Z…",
  "refunded_cents": 0
}

Errors, and what to do about each one

Tool failures come back as a normal result with isError: true and a JSON body, so your agent can read them and act. Every rejection has its own code, because each one calls for something different.

errormeaningwhat to do
unauthorizedNo API key, or a revoked one.Sign up, top up, and send the key as a Bearer token.
rejected_screeningThe brief tripped the prohibited-use ruleset.Nothing was debited. Do not retry with reworded text.
unavailableThe offering is inactive or every concurrency slot is taken.Read next_slot_expected_at, or pick another offering.
pausedBookings are paused site-wide.Retry later. Discovery still works.
exceeds_capThe price is above the account's per-booking cap.A person must raise the cap in the dashboard. Do not retry.
insufficient_balanceThe balance will not cover the price.The error carries topup_url and shortfall_cents. A person tops up.
rate_limitedMore than 120 calls in an hour on this key.Back off for retry_after_seconds.
daily_booking_limitThe account's 24-hour booking ceiling is reached.Stop booking. This is a runaway-loop guard.
invalid_callback_urlcallback_url was not a public https endpoint.Fix it, or omit it and poll get_booking instead.
not_foundNo such offering or booking.Call list_offerings again.
insufficient_balance
{
  "error": "insufficient_balance",
  "message": "Balance is too low for this booking. Top up and retry.",
  "price_cents": 12000,
  "balance_cents": 4500,
  "shortfall_cents": 7500,
  "topup_url": "https://loopin.works/dashboard#topup"
}

Money and refunds

Balances are prepaid. A person signs up, tops up through Stripe Checkout, and sets a per-booking cap. After that the agent spends against the balance on its own, inside that cap.

Every amount in the API is integer US cents. 12000 is $120. There are no floats and no currency conversion.

Refunds credit the balance, never the card:

situationrefundstatus becomes
Provider cancelsfullcancelled_by_provider
Brief is outside the offering's scopefullcancelled_out_of_scope
Delivery misses due_atfull, automaticrefunded_late

the late-refund rule

A booking past due_at is refunded in full without anyone asking, and the work is still delivered. So refunded_late is not a failure state to give up on — keep polling, the deliverable still arrives. A turnaround with no consequence would just be a hope.

Webhooks

Supply callback_url on book_offering and we POST to it when the booking reaches a terminal state. Polling get_booking works just as well, and is the right choice if you have nowhere to receive a request.

POST your callback_url
{
  "booking_ref": "LW-7K2M",
  "status": "delivered",
  "due_at": "2026-07-26T09:14:03Z",
  "delivered_at": "2026-07-26T04:41:55Z",
  "deliverable": "## Review\n\n**High** `src/queue.ts:88` — …",
  "refunded_cents": 0,
  "event": "delivered"
}
  • event is one of delivered, cancelled, refunded_late.
  • Must be https and publicly routable. Private and loopback addresses are refused at booking time.
  • One attempt, ten second timeout, no retries in this version. Poll get_booking if the callback is load-bearing.

Limits and screening

  • Rate limit. 120 tool calls per hour per API key, plus a daily booking ceiling per account.
  • Capacity. Each offering has a maximum number of bookings in flight. At the limit it reports available: false and booking is refused.
  • Screening. Briefs run through a fixed ruleset before a booking is accepted. Requests involving bulk account creation, relaying verification codes, impersonation, defeating identity checks, access to systems the requester does not own, posting under another identity, or third-party credentials are refused. Nothing is debited when that happens.
  • Privacy. We log agent client name and version, tool arguments, and a salted hash of the calling IP. Never a raw address. See privacy.
  • No provider contact details are reachable through any tool or page. Everything goes through a booking.