Building Standalone MCP Servers
Create standalone Model Context Protocol servers to add tools that don't need entity discovery.
Use a Molecule when integrating a service where entities matter (repositories, deployments, services). Molecules provide discovery, tools, skills, and visualizations in one package.
Use Standalone MCP for tools that don't need entity discovery—like querying a database, calling internal APIs, or adding utility functions.
What are MCP Tools?
MCP tools are functions that AI can call to interact with external services, query data, or perform actions. They extend what the AI can do beyond just answering questions.
MCP Server Basics
An MCP server exposes tools via HTTP, SSE, stdio, or Unix sockets. The server:
- Registers available tools with metadata
- Receives tool call requests from SixDegree
- Executes the requested tool
- Returns results
Quick Start (Python)
Install MCP SDK
pip install mcp-python
Create an MCP Server
from mcp import MCPServer, tool
server = MCPServer(name="my-tools")
@server.tool(
name="query_database",
description="Query the PostgreSQL database"
)
def query_database(sql: str) -> dict:
"""Execute SQL query and return results."""
import psycopg2
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute(sql)
rows = cur.fetchall()
return {"rows": rows, "count": len(rows)}
@server.tool(
name="send_email",
description="Send an email notification"
)
def send_email(to: str, subject: str, body: str) -> dict:
"""Send email via SMTP."""
import smtplib
# Send email logic here
return {"status": "sent", "to": to}
if __name__ == "__main__":
server.run(host="0.0.0.0", port=8000)
Quick Start (TypeScript)
Install MCP SDK
npm install @modelcontextprotocol/sdk
Create an MCP Server
import { MCPServer } from '@modelcontextprotocol/sdk';
const server = new MCPServer({
name: 'my-tools',
version: '1.0.0',
});
server.tool({
name: 'query_database',
description: 'Query the PostgreSQL database',
parameters: {
sql: {
type: 'string',
description: 'SQL query to execute',
required: true,
},
},
handler: async ({ sql }) => {
const { Pool } = require('pg');
const pool = new Pool();
const result = await pool.query(sql);
return {
rows: result.rows,
count: result.rowCount,
};
},
});
server.listen(8000);
Tool Definition
Tool Metadata
Every tool needs:
@server.tool(
name="tool_name", # Unique identifier
description="What it does", # Help AI understand when to use it
parameters={ # Input parameters
"param1": {
"type": "string",
"description": "What this parameter is",
"required": True
}
}
)
Parameter Types
Supported types:
string: Text valuesnumber: Numeric valuesboolean: True/falsearray: Listsobject: Structured data
Return Values
Tools should return structured data (JSON-serializable):
return {
"status": "success",
"data": [...],
"message": "Operation completed"
}
Authentication
Bearer Token
from mcp import MCPServer
from mcp.auth import bearer_token
server = MCPServer(
name="my-tools",
auth=bearer_token("secret-token")
)
Custom Auth
def custom_auth(request):
token = request.headers.get("X-API-Key")
if token != SECRET_KEY:
raise PermissionError("Invalid API key")
return True
server = MCPServer(
name="my-tools",
auth=custom_auth
)
Error Handling
Handle errors gracefully:
@server.tool(name="risky_operation")
def risky_operation(param: str) -> dict:
try:
result = do_something(param)
return {"status": "success", "result": result}
except ValueError as e:
return {"status": "error", "message": str(e)}
except Exception as e:
return {"status": "error", "message": "Internal error"}
Best Practices
Clear Descriptions
Help the AI understand when to use your tool:
@server.tool(
name="create_user",
description="Create a new user account in the system. Use this when the user asks to add a new user or create an account."
)
Input Validation
Validate parameters before execution:
def create_user(email: str, name: str) -> dict:
if not email or "@" not in email:
return {"status": "error", "message": "Invalid email"}
if not name or len(name) < 2:
return {"status": "error", "message": "Name too short"}
# Create user...
Structured Output
Return consistent, structured responses:
{
"status": "success" | "error",
"data": {...},
"message": "Human-readable message"
}
Deployment
Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY server.py .
EXPOSE 8000
CMD ["python", "server.py"]
Docker Compose
version: '3.8'
services:
mcp-server:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgres://...
- API_KEY=secret
Connecting to SixDegree
Once deployed, add your MCP server to SixDegree:
sixdegree mcp create \
--name "My Tools" \
--url "https://mcp.example.com" \
--transport http \
--token "secret-token"
Example: GitHub MCP Server
from mcp import MCPServer
import requests
server = MCPServer(name="github-tools")
@server.tool(
name="github_create_issue",
description="Create a new GitHub issue"
)
def create_issue(repo: str, title: str, body: str = "") -> dict:
url = f"https://api.github.com/repos/{repo}/issues"
headers = {
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
data = {"title": title, "body": body}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 201:
issue = response.json()
return {
"status": "success",
"issue_number": issue["number"],
"url": issue["html_url"]
}
else:
return {
"status": "error",
"message": response.json().get("message", "Unknown error")
}
server.run()