Custom MCP Servers
The Model Context Protocol (MCP) defines a standard interface for connecting external tools to AI applications. QARK discovers and invokes tools from any MCP-compliant server. Wrap your databases, APIs, scripts, and internal services as MCP tools, and your agents gain capabilities tailored to your workflow.
For connecting pre-built MCP servers (GitHub, Slack, Filesystem, etc.), see MCP Integration. This page covers building your own.
MCP Architecture
Section titled “MCP Architecture”An MCP server is a process or endpoint that:
- Advertises tools — Declares a manifest of available tools, each with a name, description, and typed input schema.
- Receives invocations — Accepts structured tool calls with validated parameters.
- Returns results — Sends back structured output that QARK passes to the model.
Two transport types:
| Transport | Communication | Lifecycle | Use Case |
|---|---|---|---|
| stdio | stdin/stdout JSON-RPC | QARK spawns and manages the process | Local tools, scripts, databases |
| HTTP | HTTP requests | Server runs independently | Remote APIs, shared team services |
Build a stdio Server
Section titled “Build a stdio Server”A stdio server runs as a local subprocess. QARK spawns it using a login shell (e.g., bash -l -c or zsh -l -c), which means your shell profile is loaded — nvm, pyenv, rbenv, conda, and PATH customizations all work automatically.
Configuration
Section titled “Configuration”| Field | Description | Example |
|---|---|---|
| Command | Executable path | node, python3, npx |
| Args | Arguments array | ["server.js", "--readonly"] |
| Environment variables | Key-value pairs injected into the process | { "DATABASE_URL": "postgres://..." } |
The server process only sees variables explicitly configured — it does not inherit your full shell environment beyond what the login shell profile provides.
Tool Schema
Section titled “Tool Schema”Each tool needs three components:
{ "name": "query_database", "description": "Execute a read-only SQL query against the application database. Returns results as JSON array of row objects. Maximum 1000 rows. Do not use for mutations — write operations are blocked.", "input_schema": { "type": "object", "properties": { "sql": { "type": "string", "description": "The SQL SELECT query to execute" }, "params": { "type": "array", "items": { "type": "string" }, "description": "Parameterized values to bind to $1, $2, etc." } }, "required": ["sql"] }}Write the description for an LLM audience — the model reads it to decide when and how to call the tool. Be specific about what the tool does, what it returns, and any constraints.
Example: Internal Database Tool
Section titled “Example: Internal Database Tool”Server: Node.js process connecting to PostgreSQL
{ "command": "node", "args": ["/path/to/db-server/index.js"], "env_vars": { "DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb", "MAX_ROWS": "1000" }}The server reads JSON-RPC requests from stdin, executes queries, and writes JSON-RPC responses to stdout. Diagnostic output goes to stderr (stdout is reserved for protocol messages).
Example: Python Script Wrapper
Section titled “Example: Python Script Wrapper”Wrap any Python script as an MCP tool:
{ "command": "python3", "args": ["/path/to/mcp_server.py"], "env_vars": { "API_KEY": "sk-...", "ENVIRONMENT": "production" }}Build an HTTP Server
Section titled “Build an HTTP Server”An HTTP server runs independently — on your local network, a cloud VM, or a hosted service. QARK connects over HTTP.
Configuration
Section titled “Configuration”| Field | Description | Example |
|---|---|---|
| URL | Server endpoint | https://tools.internal.company.com/mcp |
| Headers | Auth and custom headers | { "Authorization": "Bearer sk-...", "X-Team": "engineering" } |
Headers are stored with AES-256 encryption on disk.
Authentication Patterns
Section titled “Authentication Patterns”- Bearer token —
Authorization: Bearer <token>header. Most common. - API key — Custom header like
X-API-Key: <key>. - Mutual TLS — For high-security environments with client certificates.
Example: Team API Wrapper
Section titled “Example: Team API Wrapper”An HTTP server wrapping your internal project management API:
{ "url": "https://tools.internal.company.com/mcp", "headers": { "Authorization": "Bearer sk-proj-abc123", "X-Team": "engineering" }}Tools exposed:
[ { "name": "list_tickets", "description": "List open tickets for a project. Returns ticket ID, title, assignee, and status.", "input_schema": { "type": "object", "properties": { "project_id": { "type": "string", "description": "Project identifier (e.g., 'BACKEND', 'MOBILE')" }, "status": { "type": "string", "enum": ["open", "in_progress", "review", "done"], "description": "Filter by status. Omit for all." } }, "required": ["project_id"] } }, { "name": "get_ticket_details", "description": "Full details for a specific ticket including description, comments, and linked pull requests.", "input_schema": { "type": "object", "properties": { "ticket_id": { "type": "string", "description": "Ticket identifier (e.g., 'BACKEND-1234')" } }, "required": ["ticket_id"] } }]Connect to QARK
Section titled “Connect to QARK”Two methods to add MCP servers:
Settings UI
Section titled “Settings UI”- Open Settings → Tools & MCP → MCP Servers
- Click Add Server
- Select transport type (stdio or HTTP)
- Fill in configuration fields
- Save and connect
JSON Import
Section titled “JSON Import”Paste or import a JSON configuration. QARK accepts the Claude Desktop / Cursor mcpServers wrapper format:
{ "mcpServers": { "my-database": { "command": "node", "args": ["server.js"], "env": { "DATABASE_URL": "postgres://..." } } }}And flat format:
{ "my-database": { "command": "node", "args": ["server.js"], "env": { "DATABASE_URL": "postgres://..." } }}This makes it straightforward to reuse MCP server configurations from other tools.
Server Lifecycle
Section titled “Server Lifecycle”- Auto-connect on launch — Enabled servers connect automatically in the background when QARK starts. Connection happens asynchronously and does not block app startup.
- Connection states —
disconnected→connecting→connected(orerror). - Tool discovery — On connection, QARK queries the server’s tool manifest and lists discovered tools with names, descriptions, and input schemas.
- Graceful shutdown — On app exit or manual disconnect, QARK sends cancellation tokens and waits for the server process to terminate cleanly.
- Reconnect — If a server disconnects unexpectedly, reconnect manually from Settings or restart the app.
Test and Debug
Section titled “Test and Debug”Verify Tools
Section titled “Verify Tools”After connecting, check that your tools appear in the @mention popover as mcp:{server_name}:{tool_name}. Start a conversation, type @, and look for your server’s tools in the MCP section.
Check Invocation
Section titled “Check Invocation”- Ask a question that should trigger your tool
- Expand the tool call block in the conversation
- Verify: correct tool invoked, correct parameters, expected response
Common Issues
Section titled “Common Issues”| Symptom | Cause | Fix |
|---|---|---|
Server stays connecting | Command path wrong or executable missing | Verify command exists and is executable. Run manually in terminal. |
ENOENT on connect | Runtime not installed | Install Node.js, Python, etc. and verify it is in PATH. |
| HTTP connection refused | Server not running or URL incorrect | Check URL, verify server process, check firewall rules. |
| Auth error (401/403) | Invalid or expired credentials | Update headers in MCP settings. |
| Tool returns error | Server-side exception | Check server stderr/logs for stack trace. |
| Parameters malformed | Model sent unexpected input | Improve input_schema with stricter types, enums, and descriptions. |
| Missing env vars | Server does not inherit shell env | Add all required variables explicitly in the server configuration. |
| nvm/pyenv command not found | Login shell not loading profile | QARK uses login shell — verify your profile sets up the tool correctly. |