MCP servers
Connect a Model Context Protocol server you run, or a third party's, and its tools appear to the model exactly like a built-in one.
MCP (Model Context Protocol) is an open standard for a server that exposes tools to a model. Register one here, name it on a chat completion, and its tools are fetched, translated into the same shape as a function tool, and executed on your behalf when the model calls them — you never see the protocol.
headers you configure, such as an API key your server expects, are sent on every call and are never echoed back by a later read.The transport
One JSON-RPC 2.0 request per call, over streamable HTTP — the single-URL transport, not stdio and not the older two-endpoint SSE transport. When you register a server, Larsa calls tools/list immediately to publish its schema; when the model uses one of its tools, Larsa calls tools/call. There is no separate initialize handshake before either — point url at a server that answers those two methods directly.
Registering a server
/v1/mcp/servers| Parameter | Type | Description |
|---|---|---|
| label Required | string | [A-Za-z0-9_-]{1,40}. Becomes the prefix on every one of this server's tool names, so two servers offering search never collide. |
| url Required | string | Your server's streamable-HTTP endpoint. |
| headers Optional | object | Sent with every request to your server — an API key it expects, for instance. |
{
"label": "docs-lookup",
"registered": true,
"reachable": true,
"tools": ["mcp__docs-lookup__search", "mcp__docs-lookup__fetch"]
}Registration succeeds even when the server cannot be reached at that moment — reachable is false and an error explains why, rather than the whole call failing. Larsa tries tools/list again the next time you use it.
Listing its tools
/v1/mcp/{label}/tools{
"object": "list",
"data": [{
"type": "function",
"function": {
"name": "mcp__docs-lookup__search",
"description": "…",
"parameters": { "type": "object", "properties": { "query": {"type": "string"} } }
}
}]
}Register and list, together
# Register the server.
curl https://api.console.larsa.larsima.com/v1/mcp/servers \
-H "Authorization: Bearer $LARSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "docs-lookup",
"url": "https://tools.example.com/mcp",
"headers": {"Authorization": "Bearer example-server-key"}
}'
# List the servers on this account.
curl https://api.console.larsa.larsima.com/v1/mcp/servers \
-H "Authorization: Bearer $LARSA_API_KEY"import os
import requests
API = "https://api.console.larsa.larsima.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['LARSA_API_KEY']}",
"Content-Type": "application/json"}
# Register the server.
reg = requests.post(f"{API}/mcp/servers", headers=HEADERS, json={
"label": "docs-lookup",
"url": "https://tools.example.com/mcp",
"headers": {"Authorization": "Bearer example-server-key"},
})
print(reg.json())
# List the servers on this account.
servers = requests.get(f"{API}/mcp/servers", headers=HEADERS).json()
for s in servers["data"]:
print(s["label"], s["url"])const API = "https://api.console.larsa.larsima.com/v1";
const HEADERS = {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
};
// Register the server.
const reg = await fetch(`${API}/mcp/servers`, {
method: "POST", headers: HEADERS,
body: JSON.stringify({
label: "docs-lookup",
url: "https://tools.example.com/mcp",
headers: { Authorization: "Bearer example-server-key" },
}),
});
console.log(await reg.json());
// List the servers on this account.
const servers = await (await fetch(`${API}/mcp/servers`,
{ headers: HEADERS })).json();
for (const s of servers.data) console.log(s.label, s.url);const API = "https://api.console.larsa.larsima.com/v1";
const HEADERS = {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
};
// Register the server.
const reg = await fetch(`${API}/mcp/servers`, {
method: "POST", headers: HEADERS,
body: JSON.stringify({
label: "docs-lookup",
url: "https://tools.example.com/mcp",
headers: { Authorization: "Bearer example-server-key" },
}),
});
console.log(await reg.json());
// List the servers on this account.
const servers = await (await fetch(`${API}/mcp/servers`,
{ headers: HEADERS })).json() as
{ data: { label: string; url: string }[] };
for (const s of servers.data) console.log(s.label, s.url);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
const api = "https://api.console.larsa.larsima.com/v1"
func main() {
key := os.Getenv("LARSA_API_KEY")
// Register the server.
body, _ := json.Marshal(map[string]any{
"label": "docs-lookup",
"url": "https://tools.example.com/mcp",
"headers": map[string]string{
"Authorization": "Bearer example-server-key"},
})
req, _ := http.NewRequest("POST", api+"/mcp/servers",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
raw, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(string(raw))
// List the servers on this account.
listReq, _ := http.NewRequest("GET", api+"/mcp/servers", nil)
listReq.Header.Set("Authorization", "Bearer "+key)
listResp, _ := http.DefaultClient.Do(listReq)
listRaw, _ := io.ReadAll(listResp.Body)
listResp.Body.Close()
var servers map[string]any
json.Unmarshal(listRaw, &servers)
for _, s := range servers["data"].([]any) {
row := s.(map[string]any)
fmt.Println(row["label"], row["url"])
}
}use serde_json::json;
use std::env;
fn main() {
let key = env::var("LARSA_API_KEY").unwrap();
let client = reqwest::blocking::Client::new();
let api = "https://api.console.larsa.larsima.com/v1";
// Register the server.
let reg: serde_json::Value = client
.post(format!("{api}/mcp/servers"))
.bearer_auth(&key)
.json(&json!({
"label": "docs-lookup",
"url": "https://tools.example.com/mcp",
"headers": {"Authorization": "Bearer example-server-key"}
}))
.send().unwrap().json().unwrap();
println!("{reg}");
// List the servers on this account.
let servers: serde_json::Value = client
.get(format!("{api}/mcp/servers"))
.bearer_auth(&key).send().unwrap().json().unwrap();
for s in servers["data"].as_array().unwrap() {
println!("{} {}", s["label"], s["url"]);
}
}import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class McpRegister {
public static void main(String[] args) throws Exception {
String api = "https://api.console.larsa.larsima.com/v1";
String key = System.getenv("LARSA_API_KEY");
HttpClient client = HttpClient.newHttpClient();
// Register the server.
JSONObject body = new JSONObject()
.put("label", "docs-lookup")
.put("url", "https://tools.example.com/mcp")
.put("headers", new JSONObject().put(
"Authorization", "Bearer example-server-key"));
HttpRequest regReq = HttpRequest.newBuilder(
URI.create(api + "/mcp/servers"))
.header("Authorization", "Bearer " + key)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
System.out.println(client.send(regReq,
HttpResponse.BodyHandlers.ofString()).body());
// List the servers on this account.
HttpRequest listReq = HttpRequest.newBuilder(
URI.create(api + "/mcp/servers"))
.header("Authorization", "Bearer " + key)
.GET().build();
JSONObject servers = new JSONObject(client.send(listReq,
HttpResponse.BodyHandlers.ofString()).body());
JSONArray data = servers.getJSONArray("data");
for (int i = 0; i < data.length(); i++) {
JSONObject s = data.getJSONObject(i);
System.out.println(s.getString("label") + " "
+ s.getString("url"));
}
}
}using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
const string Api = "https://api.console.larsa.larsima.com/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("LARSA_API_KEY"));
// Register the server.
var regBody = new JsonObject {
["label"] = "docs-lookup",
["url"] = "https://tools.example.com/mcp",
["headers"] = new JsonObject {
["Authorization"] = "Bearer example-server-key" },
};
var reg = await http.PostAsync($"{Api}/mcp/servers",
new StringContent(regBody.ToJsonString(), Encoding.UTF8,
"application/json"));
Console.WriteLine(await reg.Content.ReadAsStringAsync());
// List the servers on this account.
var servers = JsonNode.Parse(
await http.GetStringAsync($"{Api}/mcp/servers"))!.AsObject();
foreach (var s in servers["data"]!.AsArray())
Console.WriteLine($"{s!["label"]} {s["url"]}");<?php
$api = "https://api.console.larsa.larsima.com/v1";
$key = getenv("LARSA_API_KEY");
// Register the server.
$ch = curl_init("$api/mcp/servers");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $key",
"Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"label" => "docs-lookup",
"url" => "https://tools.example.com/mcp",
"headers" => ["Authorization" => "Bearer example-server-key"],
]),
]);
echo curl_exec($ch), "\n";
curl_close($ch);
// List the servers on this account.
$ch = curl_init("$api/mcp/servers");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $key"],
]);
$servers = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($servers["data"] as $s) {
echo "{$s['label']} {$s['url']}\n";
}require "json"
require "net/http"
require "uri"
API = "https://api.console.larsa.larsima.com/v1"
KEY = ENV.fetch("LARSA_API_KEY")
def call(method, path, body = nil)
uri = URI("#{API}#{path}")
req = method.new(uri, "Authorization" => "Bearer #{KEY}",
"Content-Type" => "application/json")
req.body = body.to_json if body
JSON.parse(Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }.body)
end
# Register the server.
reg = call(Net::HTTP::Post, "/mcp/servers", {
label: "docs-lookup",
url: "https://tools.example.com/mcp",
headers: { Authorization: "Bearer example-server-key" },
})
puts reg
# List the servers on this account.
servers = call(Net::HTTP::Get, "/mcp/servers")
servers["data"].each { |s| puts "#{s['label']} #{s['url']}" }Using it from a chat completion
[{
"type": "mcp",
"server_label": "docs-lookup",
"allowed_tools": ["search"]
}]allowed_tools is optional and matches the server's own short names, unprefixed — leave it out to offer every tool the server has. An unreachable server at request time simply loses its tools for that call rather than failing the request; only larsa-auto runs MCP tools.curl https://api.console.larsa.larsima.com/v1/chat/completions \
-H "Authorization: Bearer $LARSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Use docs-lookup to find our internal deployment guide for the staging environment."}],
"tools": [{"type": "mcp", "server_label": "docs-lookup",
"allowed_tools": ["search"]}]
}'import os
import requests
API = "https://api.console.larsa.larsima.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['LARSA_API_KEY']}",
"Content-Type": "application/json"}
res = requests.post(f"{API}/chat/completions", headers=HEADERS, json={
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Use docs-lookup to find our internal deployment "
"guide for the staging environment."}],
"tools": [{"type": "mcp", "server_label": "docs-lookup",
"allowed_tools": ["search"]}],
})
print(res.json()["choices"][0]["message"]["content"])const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "larsa-auto",
messages: [{ role: "user", content:
"Use docs-lookup to find our internal deployment " +
"guide for the staging environment." }],
tools: [{ type: "mcp", server_label: "docs-lookup",
allowed_tools: ["search"] }],
}),
});
const out = await res.json();
console.log(out.choices[0].message.content);const API = "https://api.console.larsa.larsima.com/v1";
const res = await fetch(`${API}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LARSA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "larsa-auto",
messages: [{ role: "user", content:
"Use docs-lookup to find our internal deployment " +
"guide for the staging environment." }],
tools: [{ type: "mcp", server_label: "docs-lookup",
allowed_tools: ["search"] }],
}),
});
const out = (await res.json()) as any;
console.log(out.choices[0].message.content);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "larsa-auto",
"messages": []map[string]string{{"role": "user", "content":
"Use docs-lookup to find our internal deployment " +
"guide for the staging environment."}},
"tools": []map[string]any{{"type": "mcp",
"server_label": "docs-lookup",
"allowed_tools": []string{"search"}}},
})
req, _ := http.NewRequest("POST",
"https://api.console.larsa.larsima.com/v1/chat/completions",
bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("LARSA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
var out map[string]any
json.Unmarshal(raw, &out)
choice := out["choices"].([]any)[0].(map[string]any)
fmt.Println(choice["message"].(map[string]any)["content"])
}use serde_json::json;
use std::env;
fn main() {
let key = env::var("LARSA_API_KEY").unwrap();
let client = reqwest::blocking::Client::new();
let out: serde_json::Value = client
.post("https://api.console.larsa.larsima.com/v1/chat/completions")
.bearer_auth(key)
.json(&json!({
"model": "larsa-auto",
"messages": [{"role": "user", "content":
"Use docs-lookup to find our internal deployment guide for the staging environment."}],
"tools": [{"type": "mcp",
"server_label": "docs-lookup",
"allowed_tools": ["search"]}]
}))
.send().unwrap().json().unwrap();
println!("{}", out["choices"][0]["message"]["content"]);
}import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class McpUse {
public static void main(String[] args) throws Exception {
JSONObject body = new JSONObject()
.put("model", "larsa-auto")
.put("messages", new JSONArray().put(new JSONObject()
.put("role", "user")
.put("content",
"Use docs-lookup to find our internal deployment guide for the staging environment.")))
.put("tools", new JSONArray().put(new JSONObject()
.put("type", "mcp")
.put("server_label", "docs-lookup")
.put("allowed_tools", new JSONArray()
.put("search"))));
HttpRequest req = HttpRequest.newBuilder(URI.create(
"https://api.console.larsa.larsima.com/v1/chat/completions"))
.header("Authorization", "Bearer " + System.getenv("LARSA_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
HttpResponse<String> res = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
JSONObject out = new JSONObject(res.body());
System.out.println(out.getJSONArray("choices")
.getJSONObject(0).getJSONObject("message")
.getString("content"));
}
}using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
Environment.GetEnvironmentVariable("LARSA_API_KEY"));
var body = new JsonObject {
["model"] = "larsa-auto",
["messages"] = new JsonArray {
new JsonObject { ["role"] = "user", ["content"] =
"Use docs-lookup to find our internal deployment " +
"guide for the staging environment." },
},
["tools"] = new JsonArray {
new JsonObject { ["type"] = "mcp",
["server_label"] = "docs-lookup",
["allowed_tools"] = new JsonArray { "search" } },
},
};
var res = await http.PostAsync(
"https://api.console.larsa.larsima.com/v1/chat/completions",
new StringContent(body.ToJsonString(), Encoding.UTF8,
"application/json"));
var out = JsonNode.Parse(
await res.Content.ReadAsStringAsync())!.AsObject();
Console.WriteLine(out["choices"]![0]!["message"]!["content"]);<?php
$ch = curl_init("https://api.console.larsa.larsima.com/v1/chat/completions");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("LARSA_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"model" => "larsa-auto",
"messages" => [["role" => "user", "content" =>
"Use docs-lookup to find our internal deployment " .
"guide for the staging environment."]],
"tools" => [["type" => "mcp",
"server_label" => "docs-lookup",
"allowed_tools" => ["search"]]],
]),
]);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $out["choices"][0]["message"]["content"], "\n";require "json"
require "net/http"
require "uri"
uri = URI("https://api.console.larsa.larsima.com/v1/chat/completions")
req = Net::HTTP::Post.new(uri,
"Authorization" => "Bearer #{ENV.fetch('LARSA_API_KEY')}",
"Content-Type" => "application/json")
req.body = {
model: "larsa-auto",
messages: [{ role: "user", content:
"Use docs-lookup to find our internal deployment "\
"guide for the staging environment." }],
tools: [{ type: "mcp", server_label: "docs-lookup",
allowed_tools: ["search"] }],
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)["choices"][0]["message"]["content"]Removing a server
/v1/mcp/servers/{label}The same request shape as everywhere else on this page, just DELETE and no body. Every request naming this server_label afterward simply gets none of its tools.