Machine documentation
Quickstart: connect an AI agent to the MCP server
From the 4bl1ty repository to a first successful call against the MCP server, in four steps. All four are local, and that is the point: the server is not hosted, so there is no remote URL to aim at and no key to request.
Requirements#
- Node.js 18 or newer. The server’s HTTP client uses the global
fetch, which earlier versions do not have. - The project repository, with its two standalone programs:
api/(Express + Prisma + Stripe) andmcp-server/. - An MCP-capable client able to spawn a local process and talk to it over
stdio.
1. Start the API#
The API reads its .env file at the repository root. Four variables are enough for everything that does not touch a real payment:
# .env, at the repository root — read by api/src/config.ts
API_PORT=3001
JWT_SECRET=replace-with-a-long-random-value
STRIPE_SECRET_KEY=
STRIPE_PLATFORM_FEE_PERCENT=10cd api
npm install
npm run db:generate
npm run db:push
npm run devCheck that it answers before going further:
curl -s http://localhost:3001/health{"status":"ok","timestamp":"2026-08-26T09:12:44.108Z","version":"1.0.0"}2. Build the MCP server#
cd mcp-server
npm install
npm run buildThe entry point is mcp-server/dist/index.js. To iterate without rebuilding, npm run dev runs the same thing under tsx watch — but an MCP client should point at the compiled file, which starts faster and carries no dev dependencies.
3. Declare the server to your client#
The shape is the same everywhere: one entry in an mcpServers table, a command, its arguments, and the environment handed to the process. The path must be absolute — the client does not necessarily start from the repository.
{
"mcpServers": {
"4bl1ty": {
"command": "node",
"args": ["/absolute/path/to/4bl1ty/mcp-server/dist/index.js"],
"env": {
"API_URL": "http://localhost:3001"
}
}
}
}On the command line, the same declaration reads:
claude mcp add 4bl1ty \
--env API_URL=http://localhost:3001 \
-- node /absolute/path/to/4bl1ty/mcp-server/dist/index.jsAPI_URL is the only variable the server reads. Without it, it aims at http://localhost:3001.
4. First call, by hand#
The stdio transport carries JSON-RPC 2.0, one message per line. So you can talk to the server with no client at all, which is the shortest way to find out whether the whole chain works. The sequence is always the same: negotiation, initialised notification, then the calls.
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.0.1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_annonces","arguments":{"limit":3}}}Runnable as is — the output contains the list of all 17 tools with their input schemas:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.0.1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| API_URL=http://localhost:3001 node mcp-server/dist/index.jsA tool call goes through tools/call, passing the name and the arguments — that is the fourth message in the example above. list_annonces is the right tool for this test: it reads the marketplace and needs no session.
Minimal agent sequence#
The order of calls to run a task end to end, from the agent’s side. The line missing in the middle is not an omission on this page: it is a tool that does not exist.
login_user { "email": "…", "password": "…" }
get_current_user {}
create_annonce { "title": "…", "description": "…", "category": "…", "price": 2500 }
list_candidatures { "role": "customer", "status": "pending" }
update_candidature_status { "id": "…", "status": "confirmed" }
pay_candidature { "id": "…" }
# — releasing the escrow has no tool —
add_review { "bookingId": "…", "rating": 5 }Two walls on this path. create_annonce and pay_candidature both require an account whose identity verification has passed, and no tool can start it. pay_candidature then returns a Stripe Checkout URL, which an agent cannot walk through on its own. Details in What is missing.
Troubleshooting#
The API answers in French, so several of these symptoms are French strings. They are reproduced verbatim: that is what you will actually see in the error field.
| Symptom | Cause |
|---|---|
| fetch failed / ECONNREFUSED | The API is not listening, or API_URL points at the wrong port. |
| Token d'authentification manquant | No session in this process. Call login_user, then get_current_user to confirm. The API answers in French — this message means the Authorization header was missing. |
| Token invalide ou expiré | The JWT no longer matches the API's JWT_SECRET — typically after a change to .env. |
| KYC non validé | The account is not verified. No MCP tool starts the verification: this is a wall, not a delay. |
| Seuls les clients peuvent publier… | The account has the provider role. The role is chosen at registration and no tool changes it. |
| Server error (500) on list_annonces | sortBy carries an unknown field name. The only safe values are price, createdAt and title. |
| Transition de 'x' vers 'y' non autorisée | The application state machine refuses that jump. The transition table is on the HTTP reference page. |