Build MCP servers with IBM Bob
Learn to use IBM Bob to build a custom Model Context Protocol (MCP) server that connects AI models to external tools and data sources. Covers advanced mode, approval workflow, and MCP configuration in this hands-on tutorial.
In this tutorial, you use IBM Bob to build a custom MCP server that provides read-only access to arXiv, an open-access content repository for scientific papers. Optionally, you can extend the server to integrate with a watsonx Orchestrate AI agent.
The Model Context Protocol (MCP) is an open standard that enables large language models (LLMs) to communicate with external tools, data sources, and content repositories through a unified client-server architecture. Before MCP, every AI assistant needed its own bespoke integration for each external tool, using function calling with no interoperability. Instead, MCP defines a single JSON-RPC 2.0 protocol that any MCP host can use to connect to any MCP server.
Prerequisites
This tutorial builds a TypeScript MCP server that queries the arXiv API. This tutorial requires no prior TypeScript or MCP integration experience.
To complete this tutorial, you need the following:
IBM Bob IDE
Download and install the IBM Bob application on your computer. Bob is a standalone IDE application and not an extension.
Node.js
Install Node.js 22 or later to build and run the TypeScript MCP server locally.
Set up your workspace
Launch IBM Bob, open the MCP settings panel, and prepare a working directory for the server project.
Launch IBM Bob
Launch the IBM Bob application on your computer.
Open the Bob chat panel
If the chat panel is not already open, click the Bob icon beside the navigation bar or use the shortcut Option + Command + B (Mac) or Ctrl + Alt + B (Windows).
Open the MCP settings panel
Click the gear icon in the upper right corner of the chat window, then select MCP from the left sidebar.
The MCP settings panel lets you manage access control by enabling or disabling servers, auto-approving specific tools, and building custom integrations with the MCP SDK.
- Global: Stored in
mcp_settings.json, applied across all workspaces. - Project: Stored in
.bob/mcp.jsonat the project root, shareable with your team via version control. Project-level settings override global ones.
Configure auto-approval
In the Bob chat, ake sure the auto-approval permissions just below the chat input field are set to "Read" only. This configuration lets Bob view your files and directory content, while requesting your review and approval before Bob runs each command.
Open your project directory
If you have a preferred directory for the project, open it in the IDE. You can also ask Bob to do it in the chat window.
Set up a Python virtual environment
It is common practice to create virtual Python environments to isolate a project's dependencies so that different projects don't conflict with each other. Switch Bob to Agent mode, the mode that can read, write, and run terminal commands, then create the environment.
Create and activate the virtual environment
In the Bob chat panel, enter the following prompt:
In this directory, activate a Python virtual environment.Bob runs a series of terminal commands. Approve each one when prompted. The commands create a new virtual environment in the venv/ directory and activate it.
Generate the MCP server build plan
Switch to Plan mode
Click the button just below the chat input field to change the mode for interaction to Plan. This mode lets Bob generate a structured plan for the MCP server before writing any code.
Submit the server requirements
With the virtual environment active, submit the following prompt to Bob. Providing specific requirements up front gives Bob enough context to formulate a complete plan before writing any code:
Create an MCP server named arxiv-server that provides read-only access to arXiv. The server should:
- Expose one tool, search_papers, for querying arXiv papers by keyword
- Accept a query string and an optional max_results parameter (default 5, max 20)
- Limit results to paper metadata and abstracts (no PDFs)
- Return title, authors, publication date, abstract, and arXiv URL for each result
- Normalize responses into a clean, structured JSON schema
- Handle API errors and empty results gracefully, returning a clear message instead of failing
- Use TypeScript/Node.js with the stdio transport
- Use the arXiv API v2, which requires no authenticationBob produces a structured to-do list that covers project scaffolding, MCP server implementation, dependency installation, server configuration, and testing. Notice that Bob plans for error handling and authentication considerations automatically. Even when the target API (arXiv) does not require a key, Bob notes where credentials would be injected for servers that do.
If Bob asks clarifying questions, answer them to the best of your ability or tell Bob to make reasonable assumptions.
Build and review the MCP server
Once you review and approve the plan, switch to Agent mode to execute each step. Your output and order may vary slightly from the following example, since Bob generates responses in real time.
Tell Bob to start building the server with the following prompt:
Implement the plan.First, Bob scaffolds the project structure and runs mkdir -p arxiv-server/src to create the project directory.
Next, Bob generates arxiv-server/package.json, the Node.js configuration hub that declares the project's metadata, scripts, and dependencies:
{
"name": "arxiv-server",
"version": "0.1.0",
"description": "MCP server for read-only access to arXiv papers",
"type": "module",
"bin": {
"arxiv-server": "./build/index.js"
},
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch"
},
"keywords": ["mcp", "arxiv", "research", "papers"],
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.4",
"axios": "^1.7.9",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "^22.10.5",
"typescript": "^5.7.3"
}
}Bob also creates arxiv-server/tsconfig.json to configure the TypeScript compiler:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}Next, Bob creates the main server file at arxiv-server/src/index.ts. This file registers the search_arxiv tool with the MCP SDK, implements XML-to-JSON parsing for arXiv's API responses, enforces result limits, and starts the server on the STDIO transport, the local, low-latency transport type suited for servers running on the same machine as the MCP host.
The MCP SDK's server.tool() call is the primary integration point. It exposes the tool to any MCP client.
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from 'axios';
interface ArxivEntry {
id: string;
title: string;
summary: string;
authors: Array<{ name: string }>;
published: string;
updated: string;
categories: string[];
primary_category: string;
links: Array<{ href: string; rel: string; type?: string }>;
}
interface ArxivSearchResult {
entries: ArxivEntry[];
totalResults: number;
startIndex: number;
itemsPerPage: number;
}
const server = new McpServer({ name: "arxiv-server", version: "0.1.0" });
const arxivApi = axios.create({
baseURL: 'http://export.arxiv.org/api',
timeout: 30000,
});
server.tool(
"search_arxiv",
{
query: z.string().describe("Search query (supports arXiv query syntax)"),
max_results: z.number().min(1).max(50).optional()
.describe("Maximum results to return (1–50, default: 10)"),
start: z.number().min(0).optional()
.describe("Starting index for pagination (default: 0)"),
sort_by: z.enum(["relevance", "lastUpdatedDate", "submittedDate"]).optional(),
sort_order: z.enum(["ascending", "descending"]).optional()
},
async ({ query, max_results = 10, start = 0, sort_by = "relevance", sort_order = "descending" }) => {
try {
const params: Record<string, string | number> = {
search_query: query,
start: Math.max(start, 0),
max_results: Math.min(max_results, 50),
...(sort_by && { sortBy: sort_by }),
...(sort_order && { sortOrder: sort_order }),
};
const response = await arxivApi.get('/query', { params });
return {
content: [{ type: "text", text: formatSearchResults(parseArxivXML(response.data)) }],
};
} catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [{ type: "text", text: `arXiv API error: ${error.response?.data?.message ?? error.message}` }],
isError: true,
};
}
throw error;
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('arXiv MCP server running on stdio');The isError: true flag in the catch block is MCP's standard error handling pattern. It signals to the MCP client that the tool call failed without crashing the server process.
As a next step, Bob installs the dependencies within the arxiv-server directory by running cd arxiv-server && npm install.
Register the server at project scope
Bob does not register a new server automatically. Tell Bob to add it explicitly, and specify project scope so the configuration lives in .bob/mcp.json and can be shared with your team through version control.
Tell Bob to register the server
In the Bob chat panel, enter the following prompt:
Register the arxiv-server as an MCP server at project scope. Build it first if needed, then add it to .bob/mcp.json.Review the generated configuration
Bob writes the following to .bob/mcp.json at your project root. The command and args fields tell the MCP client how to start the server process using the STDIO transport.
{
"mcpServers": {
"arxiv-server": {
"command": "node",
"args": ["${workspaceFolder}/arxiv-server/build/index.js"]
}
}
}Confirm the server is loaded
Bob reloads the MCP configuration automatically after writing this file. Open the MCP settings panel (gear icon > MCP) to confirm the arxiv-server server is listed and enabled before continuing. If it doesn't appear, click the reload icon next to the server list.
Restart Bob
Restart Bob to ensure the server is running and ready to accept queries.
Test the MCP server
With the server registered, Bob automatically runs two validation queries against the search_arxiv tool.
The first test queries for three quantum computing papers sorted by relevance. The second queries for two machine learning papers sorted by descending submission date. Both execute successfully, confirming the tool is reachable and that the server's error handling correctly manages different parameter combinations.
Now run your own queries to verify that Bob extracts the correct parameters from natural language. An example of a prompt to paste into the Bob chat panel can be:
What are the latest papers on LLM agent tracing?Document the server
Open source MCP server implementations typically include documentation so others can get started quickly. Ask Bob to generate it:
In this directory, create a README.md file to document this MCP server.
Include setup and usage instructions.Bob produces a comprehensive README.md covering installation, configuration for multiple MCP hosts (IBM Bob, Claude Desktop, Cursor, Claude Code), authentication guidance for servers that require API keys, local file access patterns, and troubleshooting tips.
Clean up resources
This tutorial creates local files and a server registration. Remove them if you don't plan to keep using the arXiv MCP server.
Open the MCP settings panel in Bob (gear icon > MCP) and disable or delete the arxiv-server entry. Alternatively, remove the arxiv-server block from mcp_settings.json (global scope) or .bob/mcp.json (project scope) directly.
Next steps
In this tutorial, you used IBM Bob to build a TypeScript MCP server, configure it with STDIO transport, and test it with live arXiv queries, all through natural language prompts.
The same workflow applies to more complex MCP server implementations: servers that connect to databases, local files, or any other external data source. Servers requiring authentication need credentials injected as environment variables in the MCP configuration JSON. For remote deployments, swap the STDIO transport for SSE.
- Learn about Bob's Code review feature to catch issues before committing your server code.
- Learn about modes to understand when to use Advanced, Code, Ask, and other Bob personas.
- Explore MCP configuration for details on global vs. project scope, auto-approved tools, and SSE transport setup.
- Work through the Get started with IBM Bob tutorial series to continue learning.
Modernize a Node.js application
Learn to use IBM Bob for application modernization by upgrading a Node.js Express API from version 16 to 22. Try AI-assisted development with modes, approvals, and literate coding in this hands-on tutorial.
Audit code and generate reports
Use IBM Bob to create a reusable security audit skill, scan an application against OWASP ASVS requirements, and generate SARIF and OSCAL reports that developers and AI agents can act on.