Dev.to · 6 min read

How to Build an AI Agent from Your Existing REST API — Without an MCP Server

How to Build an AI Agent from Your Existing REST API — Without an MCP Server

How to Build an AI Agent from Your Existing REST API — Without an MCP Server You already have a REST API. You don't want to rewrite it, run a dedicated protocol server, or learn JSON-RPC just to let an AI agent call GET /orders or POST /users. RCP (REST Connector Protocol) is the shortest path: add one GET /manifest route that describes your endpoints as tools. Your AI agent fetches it and calls your API directly — stateless HTTP, no persistent connection. RCP is new — ChatGPT/Claude don't speak it natively. Your AI agent does. It fetches the manifest and exposes the tools to the model via the OpenAI SDK, LangChain, or Gemini. That's the whole trick. Docs: https://rcp.hasanraiyan.me GitHub: https://github.com/hasanraiyan/rcp npm: rcp-sdk — https://www.npmjs.com/package/rcp-sdk Why not just use MCP? MCP is great when you need rich, stateful capabilities — resources, prompts, elicitation, sampling, a long-lived session. That power costs a protocol server + JSON-RPC + transport negotiation. RCP is for the far more common case: "I have a REST API. I want an AI agent to call some of its endpoints as tools, and I don't want to run anything besides my existing API plus one more route that returns JSON." If you outgrow that, reach for MCP. That's a real signal, not a gap RCP tries to fill. Full comparison: https://rcp.hasanraiyan.me/docs/vs-mcp RCP MCP Connection Stateless HTTP Persistent session Server Your existing REST API (or static JSON) Dedicated protocol server Transport Plain HTTP JSON-RPC over stdio / Streamable HTTP Best fit You already have endpoints to expose Rich, stateful capabilities How it works in 3 steps Discovery: GET /manifest → { rcpVersion, auth, tools[] } Selection: Agent sees only exposedParams (resolver-bound tenantId/userId already stripped) Execution: Agent picks a tool → client fills resolvers from trusted ctx → calls the tool's own URL directly Client Server GET /manifest ─────────────► 200 { rcpVersion: "0.1", tools: [...] } ...agent picks create_order... POST https://api.acme.dev/orders ──► { id: "ord_193" } 5-minute walkthrough 1. Install npm i rcp-sdk # also: pnpm add rcp-sdk / yarn add rcp-sdk rcp-sdk ships two entry points: rcp-sdk/client (your AI agent) and rcp-sdk/server (defineTool() helper). 2. Define a tool on your server defineTool() never touches the network — it returns a plain object. You serve it. // server.ts import { defineTool } from 'rcp-sdk/server'; import { z } from 'zod'; export const getWeather = defineTool({ name: 'get_weather', description: 'Get current weather for a city.', method: 'GET', args: z.object({ city: z.string().describe('City name, e.g. "Paris"'), }), url: 'https://internal.example.com/weather', queryParams: { city: (t) => t.arg('city') }, responseMappings: { temperatureC: '@temperatureC', conditions: '@conditions', }, }); zod → params mapping is automatic: z.string().describe() becomes { name, type: 'string', description, required: true }. Typo in t.arg() is a compile error, not a silent bug. 3. Serve the manifest One route. A plain Node http server is fully conformant — no framework required. import { createServer } from 'node:http'; const manifest = { rcpVersion: '0.1', auth: { type: 'header', header: 'Authorization', scheme: 'Bearer' }, tools: [getWeather], }; createServer((req, res) => { if (req.url === '/manifest') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(manifest)); return; } // ... your /weather handler }).listen(4310); A static JSON file behind a CDN is also a valid RCP server — curl can test it. 4. Build an AI agent that uses it Option A — OpenAI SDK (ChatGPT / GPT-4o via your agent) import { createRcpClient } from 'rcp-sdk/client'; import { rcpToolsToOpenAiTools } from 'rcp-sdk/adapters/openai'; import OpenAI from 'openai'; const rcp = createRcpClient({ auth: { type: 'header', secret: process.env.SERVER_TOKEN! } }); const { tools } = await rcp.discover('http://localhost:4310/manifest'); const openaiTools = rcpToolsToOpenAiTools(tools); // ChatCompletionTool[] const openai = new OpenAI(); const completion = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'What is the weather in Paris?' }], tools: openaiTools, }); // model returns tool_call → you call rcp.call(tool, args) → feed result back → final answer Your agent speaks RCP — ChatGPT doesn't need to. Full adapter docs: https://rcp.hasanraiyan.me/docs/sdk/openai Option B — LangChain / LangGraph import { createRcpClient } from 'rcp-sdk/client'; import { loadRcpLangChainTools } from 'rcp-sdk/adapters/langchain'; import { ChatOpenAI } from '@langchain/openai'; import { createAgent } from 'langchain'; const rcp = createRcpClient(); const tools = await loadRcpLangChainTools('https://api.example.com/manifest', rcp, { context: { userId: 'u_42' }, // resolver-bound, never reaches the model serverName: 'myApi', }); const model = new ChatOpenAI({ model: 'gpt-4o-mini' }); const agent = createAgent({ model, tools }); Supports MultiServerRcpClient for multiple manifests. Docs: https://rcp.hasanraiyan.me/docs/sdk/langchain Option C — Gemini (Google GenAI) import { rcpToolsToGeminiInteractionsTools } from 'rcp-sdk/adapters/gemini'; import { GoogleGenAI } from '@google/genai'; const geminiTools = rcpToolsToGeminiInteractionsTools(tools); const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! }); const interaction = await ai.interactions.create({ model: 'models/gemini-2.5-flash', input: 'What is the weather in Paris?', tools: geminiTools, }); Both Interactions API (flat) and classic generateContent supported: https://rcp.hasanraiyan.me/docs/sdk/gemini The killer feature: params the model never sees A REST endpoint often needs tenantId or userId — but you don't want the LLM to guess or spoof it. Resolvers are client-side only — nothing declared in the manifest. const client = createRcpClient({ resolvers: { tenantId: (ctx) => ctx.tenantId, // hidden from model userId: (ctx) => ctx.currentUserId, }, }); At discover() time: tenantId is removed from exposedParams — the model has no field to fill. At call() time: client fills the real value from trusted ctx before the HTTP request leaves. No value to resolve → call fails before any HTTP goes out. MCP has no equivalent — inputSchema is plain JSON Schema, every property is model-fillable. Details: https://rcp.hasanraiyan.me/docs/concepts/resolvers Full runnable example — Express + AI agent Two standalone projects that install rcp-sdk from npm (not from this repo): git clone https://github.com/hasanraiyan/rcp.git && cd rcp/examples cd express && npm install && npm start & # your REST API + one /rcp/manifest route cd ../openai-client && npm install # the AI agent side cp .env.example .env # add your OPENAI_API_KEY npm start -- "List my tasks, then mark the first incomplete one as done." examples/express has no OpenAI code. examples/openai-client has no Express code. See https://rcp.hasanraiyan.me/docs/examples Minimal no-framework demo also lives in typescript/examples/basic — one http server, one client, pnpm example and you see Discovered 1 tool(s). When to use RCP vs MCP Use RCP when: You already have a REST API and want an AI agent to call a handful of endpoints Every call is one-shot: fetch, call, get response You need tenant/user isolation via resolvers You want any client to integrate by reading JSON, no SDK required on server side Use MCP when: Model needs to browse resources as a distinct concept from tools You need prompt templates, elicitation, or sampling Long-lived stateful session is valuable Decision checklist: https://rcp.hasanraiyan.me/docs/vs-mcp Full spec v0.1: https://rcp.hasanraiyan.me/docs/spec Try it npm i rcp-sdk Then defineTool() → serve manifest → createRcpClient().discover() → adapter → agent. 5 minutes, no protocol server. Star the repo if this is useful — it helps the protocol get found: https://github.com/hasanraiyan/rcp Canonical: https://rcp.hasanraiyan.me/docs/getting-started — originally published on rcp.hasanraiyan.me

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News