Dashboard
Tutorial

Wire your first MCP server

Erik Nguyen· Platform EngineeringMay 18, 20269 min readMCPIntegrations

MCP — Model Context Protocol — is the standard your agent uses to talk to tools that live outside Brainbase. Think of it as a USB port: any service that speaks MCP can be plugged in without bespoke glue code. This tutorial stands up a minimal MCP server, exposes one tool, and attaches it to an agent.

What MCP buys you

  • One protocol for every tool — no per-integration adapter to maintain.
  • Tool discovery happens at runtime, so swapping out a server doesn't require redeploying the agent.
  • [object Object]

1. Stand up the server

The fastest path is the reference Node MCP server. It exposes everything over a single HTTP endpoint with SSE-style streaming.

bash
npm create mcp-server@latest acme-inventory
cd acme-inventory
npm install
npm run dev

That gives you a server listening on http://localhost:3333/mcp. Hit GET /mcp/manifest to see the empty manifest — you'll fill it next.

2. Declare tools

A tool is a function the agent can call. Each declaration includes a name, a schema for the inputs, and a handler.

src/tools/lookup-sku.tstypescript
import { defineTool } from "@mcp/server";
import { z } from "zod";

export const lookupSku = defineTool({
  name: "inventory.lookup_sku",
  description: "Return current stock and price for a SKU.",
  input: z.object({
    sku: z.string().describe("The SKU, e.g. ABC-123"),
  }),
  async handler({ sku }) {
    const row = await db.skus.find({ sku });
    if (!row) return { found: false };
    return { found: true, stock: row.qty, price_cents: row.price };
  },
});

Register the tool in server.ts and restart the dev server. The manifest now lists inventory.lookup_sku with its schema.

3. Attach to an agent

Tell your agent about the server when you create it. The agent will pull the manifest on its first run and treat each entry as a callable tool.

bash
curl --request POST \
  --url https://api.brainbaselabs.com/v2/agents \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "title": "Inventory assistant",
    "instructions": "Answer stock questions using the inventory tool. Cite the SKU you looked up.",
    "mcp_servers": [
      { "name": "inventory", "url": "https://inventory.acme.internal/mcp", "auth": "bearer" }
    ]
  }'

4. Lock down permissions

Never ship a wide-open MCP server
By default the agent can call every tool in the manifest. Use scopes to restrict access — for example, allow inventory.lookup_sku but block inventory.adjust_stock unless the agent has the inventory:write scope.
mcp.config.yamlyaml
scopes:
  inventory:read:
    tools:
      - inventory.lookup_sku
      - inventory.list_low_stock
  inventory:write:
    tools:
      - inventory.adjust_stock

Assign scopes per agent in the Dashboard, or via the POST /v2/agents/{id}/scopes endpoint. The MCP server enforces them on each call — even if the agent decides to try a tool it can't see, the server refuses.

Troubleshooting

Agent says: "that tool isn't available"
Check that the agent's scope set includes the tool's scope. Re-run after updating.
401 from the MCP server
The agent forwards your bearer token verbatim. Confirm the token has access to the server.
Stuck on tool discovery
The manifest endpoint must respond in under 2 seconds. Cache it server-side if your enumeration is slow.
Tool returns but agent doesn't use it
Make the tool's description more concrete — the agent uses that string to decide whether to call it.