MCP and Cursor: Connect Your IDE to Databases and APIs
What if you could ask your IDE to query your PostgreSQL database directly, check open GitHub issues, read documentation from Confluence, and call external REST APIs, all without leaving the editor and without manually copy-pasting schemas and data? This is exactly what Model Context Protocol (MCP), natively integrated in Cursor, enables.
MCP is an open standard developed by Anthropic, launched in November 2024, that defines how language models can communicate with external systems in a structured, secure, and composable way. In just a few months it became the de facto standard for AI-to-tooling integration: Cursor adopted it natively, and today the ecosystem counts hundreds of ready-to-use MCP servers for databases, APIs, filesystems, ticketing systems, and much more.
In this article we dive deep into MCP from a practical, advanced perspective: architecture, configuration in Cursor, prebuilt servers for the most common use cases, creating custom servers in TypeScript, security management, and troubleshooting. By the end you will have everything you need to turn Cursor into an agent capable of interacting with your entire project infrastructure.
What You Will Learn
- MCP architecture: hosts, clients, servers, tools, resources, and prompts
- Configure MCP in Cursor with the
.cursor/mcp.jsonfile (project and global scope) - Connect Cursor to PostgreSQL, MySQL, and SQLite via MCP servers
- Integrate REST APIs, GraphQL, GitHub, and Jira with prebuilt MCP servers
- Build a custom MCP server in TypeScript using the official SDK
- Manage security, permissions, and credential rotation
- Diagnose and resolve the most common MCP connection issues
- Compare MCP vs traditional integration approaches
Prerequisites
- Cursor IDE installed (version 0.43+ recommended for stable MCP support)
- Node.js 18+ and npm installed
- Basic knowledge of TypeScript and JSON
- Optionally: an accessible PostgreSQL or MySQL database for hands-on examples
1. What is the Model Context Protocol
The Model Context Protocol (MCP) is an open standard built on JSON-RPC 2.0 that defines how an AI application (the client) can connect to external systems (the servers) to obtain context, execute actions, and access resources in a structured manner. It is the "common language" that allows Cursor to talk to your database the same way it might talk to GitHub, Jira, or any other API.
Before MCP, every AI IDE implemented its own proprietary integrations: different tools, different APIs, different formats. With MCP, a server written once works with Cursor, Claude Desktop, VS Code Copilot, and any other compatible client. It is the same principle that made HTTP the standard of the web.
2. MCP Architecture: Hosts, Clients, and Servers
The MCP architecture is structured across three levels that are fundamental to understand before configuring any integration:
The Three MCP Layers
- Host: the application the user interacts with directly. In this case, Cursor IDE. The host manages the lifecycle of MCP clients and orchestrates interactions with AI models.
- MCP Client: the internal component within the host that maintains a 1:1 connection with a single MCP server. Cursor automatically manages the creation and maintenance of clients for each configured server.
- MCP Server: an external (or remote) process that exposes capabilities: tools, resources, and prompts. It can be a local process (stdio) or a remote service (HTTP).
Each MCP server exposes three types of primitives that the client can use:
// MCP Primitives: conceptual overview
// 1. TOOLS - actions the model can invoke
// (reads/writes, API calls, query execution)
{
"name": "execute_query",
"description": "Executes a SQL query against the database",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" }
},
"required": ["query"]
}
}
// 2. RESOURCES - data the model can read
// (similar to GET endpoints in a REST API)
{
"uri": "postgres://mydb/tables",
"name": "Database Tables",
"description": "List of all database tables",
"mimeType": "application/json"
}
// 3. PROMPTS - reusable templates
// (pre-built instructions for common tasks)
{
"name": "analyze_schema",
"description": "Analyze database schema and suggest optimizations",
"arguments": [
{ "name": "table_name", "required": true }
]
}
Communication between client and server happens via JSON-RPC 2.0 over one of the supported transports:
- stdio (Standard I/O): the client starts the server as a child process and communicates via stdin/stdout. It is the simplest transport and is well-suited for local servers. Cursor automatically manages the process lifecycle.
- Streamable HTTP: the server runs as an independent HTTP service. Ideal for remote servers shared across multiple developers or for cloud integrations. Since March 2025 it has replaced SSE as the recommended HTTP transport.
SSE Transport Deprecated
The SSE (Server-Sent Events) transport was deprecated by the MCP specification on March 26, 2025.
If you are using existing SSE configurations, migrate to the streamableHttp format.
Cursor 0.43+ supports the new specification.
3. Configuring MCP in Cursor
Cursor supports two configuration scopes for MCP, which determine where the configured servers are available:
# GLOBAL configuration (available in all projects)
~/.cursor/mcp.json
# PROJECT configuration (only in current workspace)
.cursor/mcp.json <-- in the project root
# Project configuration takes PRECEDENCE over global
# in case of name conflicts
The basic structure of the configuration file is straightforward and consistent between both scopes:
// .cursor/mcp.json - Basic structure
{
"mcpServers": {
"server-name": {
"command": "command-to-run",
"args": ["arg1", "arg2"],
"env": {
"VARIABLE": "value"
}
}
}
}
For remote servers via HTTP, the structure is slightly different:
// .cursor/mcp.json - Remote HTTP server
{
"mcpServers": {
"remote-server-name": {
"url": "https://my-mcp-server.example.com/mcp",
"headers": {
"Authorization": "Bearer {{env.MCP_API_TOKEN}}"
}
}
}
}
After saving the configuration file, restart Cursor or press Cmd/Ctrl + Shift + P and
search for "MCP: Reload Servers" to apply the changes. You can verify the server status in
Settings > MCP where each server shows a green (connected) or red (error) indicator.
4. MCP Servers for Databases
Database MCP servers are among the most useful for developers: they allow the AI model in Cursor to inspect schemas, run queries, analyze performance, and suggest optimizations based on your actual database structure, not generic assumptions.
4.1 PostgreSQL
Anthropic's official MCP server for PostgreSQL exposes the database schema as MCP resources and allows executing read-only queries (read-only by design for security):
// .cursor/mcp.json - Official PostgreSQL server
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://user:password@localhost:5432/my_database"
]
}
}
}
Never Put Credentials Directly in the Config File
Avoid embedding username and password directly in the connection string URL within mcp.json,
especially if the file is version controlled. Use environment variables instead.
// .cursor/mcp.json - PostgreSQL with environment variables
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
# In your .env file (NOT committed to git):
DB_USER=myuser
DB_PASSWORD=supersecretpassword
Once configured, you can interact with the database directly from Cursor's chat:
Example Prompts with PostgreSQL MCP
- "Show me the schema for the users table and tell me if important indexes are missing"
- "How many rows are in the orders table created in the last month?"
- "Analyze this slow query and suggest optimizations based on the real schema"
- "Generate a TypeORM migration to add the email_verified column to the users table"
4.2 MySQL and MariaDB
Community MCP servers exist for MySQL and MariaDB that offer similar capabilities:
// .cursor/mcp.json - MySQL via community server
{
"mcpServers": {
"mysql": {
"command": "npx",
"args": ["-y", "@benborla29/mcp-server-mysql"],
"env": {
"MYSQL_HOST": "localhost",
"MYSQL_PORT": "3306",
"MYSQL_USER": "root",
"MYSQL_PASS": "password",
"MYSQL_DB": "my_database",
"ALLOW_INSERT_OPERATION": "false",
"ALLOW_UPDATE_OPERATION": "false",
"ALLOW_DELETE_OPERATION": "false"
}
}
}
}
4.3 SQLite for Local Development
// .cursor/mcp.json - SQLite (great for local dev and testing)
{
"mcpServers": {
"sqlite": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sqlite",
"--db-path",
"./database/dev.db"
]
}
}
}
5. MCP Servers for APIs and External Services
Beyond databases, the MCP ecosystem offers prebuilt servers for integrating the most common tools in your daily development workflow.
5.1 GitHub MCP
GitHub's official MCP server (maintained directly by GitHub) allows managing repositories, issues, pull requests, CI/CD workflows, and much more directly from Cursor's chat:
// .cursor/mcp.json - Official GitHub MCP (via Docker)
// NOTE: The npm package was deprecated in April 2025
// Use the official Docker image instead:
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}
// Alternative: via npx (community version)
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}
With the GitHub server configured, Cursor can:
- Read and create issues and pull requests
- Analyze commit history to understand a bug's context
- Search code in public and private repositories
- Trigger GitHub Actions workflows
- Read and write PR comments
5.2 Filesystem MCP
Anthropic's filesystem server allows the model to read and write files at specific paths, with granular access control:
// .cursor/mcp.json - Filesystem with restricted access
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/projects",
"/tmp/cursor-workspace"
]
}
}
}
// WARNING: Only specify the directories you actually need
// Never grant access to / (root) or the full home directory
5.3 Multi-Server Configuration
One of MCP's most powerful aspects in Cursor is the ability to configure multiple servers simultaneously, combining databases, external APIs, and internal tools in a single configuration file:
// .cursor/mcp.json - Complete multi-server configuration
{
"mcpServers": {
// Production database
"postgres-prod": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://user:pass@prod-db:5432/app"
}
},
// Development database
"postgres-dev": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://user:pass@localhost:5432/app_dev"
}
},
// GitHub integration
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxx"
}
},
// Project filesystem
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/projects/my-app"
]
},
// Internal custom server
"internal-api": {
"command": "node",
"args": ["/Users/username/mcp-servers/api-server/dist/index.js"],
"env": {
"API_BASE_URL": "https://api.my-company.com",
"API_KEY": "key_xxxx"
}
}
}
}
6. Building a Custom MCP Server in TypeScript
Prebuilt servers cover many common use cases, but you will often need to connect Cursor to internal systems, proprietary APIs, or project-specific logic. The official TypeScript SDK makes building custom servers surprisingly straightforward.
6.1 Project Setup
# Initialize the project
mkdir my-mcp-server
cd my-mcp-server
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node ts-node
# Configure TypeScript
npx tsc --init --target ES2022 --module commonjs --outDir dist
// package.json - required scripts
{
"name": "my-mcp-server",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"@types/node": "^20.0.0",
"ts-node": "^10.9.0"
}
}
6.2 Complete MCP Server with Tools and Resources
Let us build a complete MCP server that exposes tools for querying an internal REST API and resources for exposing static data:
// src/index.ts - Complete MCP server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const API_BASE_URL = process.env.API_BASE_URL || "https://api.example.com";
const API_KEY = process.env.API_KEY || "";
// Initialize the MCP server
const server = new Server(
{
name: "my-mcp-server",
version: "1.0.0",
},
{
capabilities: {
resources: {},
tools: {},
},
}
);
// === TOOLS ===
// Tools are actions the model can invoke
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_users",
description: "Retrieves the list of active users from the API",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Maximum number of users to return (default: 10)",
},
role: {
type: "string",
enum: ["admin", "user", "guest"],
description: "Filter by user role",
},
},
required: [],
},
},
{
name: "create_report",
description: "Generates an aggregated report for a specified time period",
inputSchema: {
type: "object",
properties: {
start_date: {
type: "string",
format: "date",
description: "Period start date (YYYY-MM-DD)",
},
end_date: {
type: "string",
format: "date",
description: "Period end date (YYYY-MM-DD)",
},
type: {
type: "string",
enum: ["sales", "users", "performance"],
description: "Type of report to generate",
},
},
required: ["start_date", "end_date", "type"],
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "get_users": {
const limit = (args?.limit as number) || 10;
const role = args?.role as string | undefined;
const url = new URL(`






