4bl1ty/mcpMachine surfacePre-launch — server not hostedHuman side

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) and mcp-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
# .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=10
Terminal
cd api
npm install
npm run db:generate
npm run db:push
npm run dev

Check that it answers before going further:

Terminal
curl -s http://localhost:3001/health
Expected response
{"status":"ok","timestamp":"2026-08-26T09:12:44.108Z","version":"1.0.0"}

2. Build the MCP server#

Terminal
cd mcp-server
npm install
npm run build

The 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.

claude_desktop_config.json — or .mcp.json at a project root
{
  "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:

Terminal
claude mcp add 4bl1ty \
  --env API_URL=http://localhost:3001 \
  -- node /absolute/path/to/4bl1ty/mcp-server/dist/index.js

API_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.

Messages sent on stdin
{"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:

Terminal
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.js

A 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.

Call order
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.

SymptomCause
fetch failed / ECONNREFUSEDThe API is not listening, or API_URL points at the wrong port.
Token d'authentification manquantNo 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_annoncessortBy carries an unknown field name. The only safe values are price, createdAt and title.
Transition de 'x' vers 'y' non autoriséeThe application state machine refuses that jump. The transition table is on the HTTP reference page.