Quick Start
Connect to Lovelace and complete your first MCP workflow
This guide walks through connecting to the public MCP Gateway, from authentication to a full agent workflow. Choose the path that matches your setup.
Fast path — Claude Code
If you have Claude Code installed, this is all you need:
claude mcp add lovelace --transport streamable-http \
--url https://mcp.uselovelace.com/mcp
Claude Code handles OAuth automatically on first use. Then ask Claude:
List my Lovelace workspaces
For full programmatic control, continue below.
Prerequisites
- A Lovelace account
- For programmatic use: Python 3.10+ or Node.js 18+
Install the MCP SDK:
# Python
pip install mcp
# TypeScript / Node.js
npm install @modelcontextprotocol/sdk
Step 1: Configure your client
Use the public gateway endpoint:
- URL:
https://mcp.uselovelace.com/mcp - Transport: Streamable HTTP
- Authorization: The client follows the gateway's
401challenge and Protected Resource Metadata discovery flow
bashclaude mcp add lovelace --transport streamable-http \ --url https://mcp.uselovelace.com/mcp
For full client-specific instructions, see Client Setup.
Step 2: Let the client authorize
On first connection, the gateway challenges the client and points it to:
https://mcp.uselovelace.com/.well-known/oauth-protected-resource
The client discovers Lovelace Accounts as the authorization server, completes OAuth 2.1 authorization, and sends bearer tokens on subsequent requests. See Authentication for the complete flow.
For programmatic clients, pass the token directly:
import os
TOKEN = os.environ["LOVELACE_TOKEN"] # from your OAuth flow
const TOKEN = process.env.LOVELACE_TOKEN!; // from your OAuth flow
Step 3: Verify access
AI client — Ask your client:
List my Lovelace workspaces
Python:
import asyncio, json, os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
TOKEN = os.environ["LOVELACE_TOKEN"]
async def main():
async with streamablehttp_client(
"https://mcp.uselovelace.com/mcp",
headers={"Authorization": f"Bearer {TOKEN}"},
) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("lovelace_list_workspaces", {})
workspaces = json.loads(result.content[0].text)
for ws in workspaces["workspaces"]:
print(ws["id"], ws["name"])
asyncio.run(main())
TypeScript:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const TOKEN = process.env.LOVELACE_TOKEN!;
const client = new Client({ name: "my-app", version: "1.0.0" });
await client.connect(
new StreamableHTTPClientTransport(
new URL("https://mcp.uselovelace.com/mcp"),
{ requestInit: { headers: { Authorization: `Bearer ${TOKEN}` } } },
),
);
const result = await client.callTool("lovelace_list_workspaces", {});
const workspaces = JSON.parse((result.content[0] as { text: string }).text);
console.log(workspaces.workspaces);
That invokes lovelace_list_workspaces and returns the workspaces you can access.
Step 4: Launch an agent
Pick a workspace ID from the previous step, then spawn an agent:
AI client:
Spawn a Lovelace agent in workspace ws_abc123 to review our API performance bottlenecks
Python:
spawn_result = await session.call_tool("lovelace_spawn_agent", {
"workspaceId": "ws_abc123",
"agentType": "general",
"task": "Review our API performance bottlenecks and list the top three",
})
agent = json.loads(spawn_result.content[0].text)
agent_id = agent["agentId"]
print(f"Spawned: {agent_id}")
TypeScript:
const spawnResult = await client.callTool("lovelace_spawn_agent", {
workspaceId: "ws_abc123",
agentType: "general",
task: "Review our API performance bottlenecks and list the top three",
});
const agent = JSON.parse((spawnResult.content[0] as { text: string }).text);
const agentId = agent.agentId as string;
console.log(`Spawned: ${agentId}`);
Step 5: Check status and fetch results
AI client:
Check the status of Lovelace agent agent_123
Get the result for Lovelace agent agent_123
Python:
import asyncio
# Poll until complete
while True:
status_result = await session.call_tool(
"lovelace_get_agent_status", {"agentId": agent_id}
)
status = json.loads(status_result.content[0].text)
if status["status"] in ("completed", "failed"):
break
await asyncio.sleep(2)
# Fetch output
output = await session.call_tool("lovelace_get_agent_result", {"agentId": agent_id})
print(json.loads(output.content[0].text))
TypeScript:
// Poll until complete
while (true) {
const statusResult = await client.callTool("lovelace_get_agent_status", {
agentId,
});
const status = JSON.parse((statusResult.content[0] as { text: string }).text);
if (status.status === "completed" || status.status === "failed") break;
await new Promise((r) => setTimeout(r, 2000));
}
// Fetch output
const output = await client.callTool("lovelace_get_agent_result", { agentId });
console.log(JSON.parse((output.content[0] as { text: string }).text));
Step 6: Read matching resources
You can also read resources directly:
lovelace://workspaceslovelace://workspaces/ws_abc123lovelace://knowledge/doc_456
What's next
- Agent Workflows — Full copy-paste examples for common agentic patterns
- Tools Reference — Exact tool contracts and scopes
- Resources Reference — Exact resource URIs and payloads
- Authentication — Protected-resource discovery and OAuth guidance
- Local Server — Local MCP server for Ada CLI/daemon workflows