MCP Server Governance
| Field | Value |
|---|---|
| Document ID | ASCEND-SDK-003 |
| Version | 2026.04 |
| Last Updated | April 2026 |
| Author | Ascend Engineering Team |
| Publisher | OW-KAI Technologies Inc. |
| Classification | Enterprise Client Documentation |
| Compliance | SOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4 |
Reading Time: 10 minutes | Skill Level: Intermediate
Overview
ASCEND provides governance for Model Context Protocol (MCP) servers, enabling control over AI tool execution. Every MCP tool invocation can be evaluated against governance policies before execution.
Pin the MCP server package in production: npm install @ascend-ai/mcp-server@2.1.0. Test MCP governance behavior in a staging environment before deploying to production.
What is MCP?
The Model Context Protocol (MCP) allows AI models like Claude to interact with external tools and data sources. ASCEND governance ensures these interactions are:
- Evaluated against risk policies
- Approved or denied based on rules
- Audited for compliance
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ MCP GOVERNANCE FLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Claude │ │ MCP Server │ │ External │ │
│ │ AI │───────▶│ + ASCEND │───────▶│ Service │ │
│ │ │ │ Middleware │ │ │ │
│ └─────────┘ └──────┬──────┘ └─────────────┘ │
│ │ │
│ Tool Call │ ASCEND Governance │
│ "execute_sql" │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ ASCEND │ │
│ │ Platform │ │
│ │ │ │
│ │ ✓ Evaluate │ │
│ │ ✓ Approve │ │
│ │ ✓ Audit │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Installation
Node.js/TypeScript
npm install @ascend-ai/sdk@2.1.0
Python
pip install ascend-ai-sdk==2.1.0
Quick Start
TypeScript MCP Server
// Source: sdk/nodejs/src/mcp.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { AscendClient, mcpGovernance, requireGovernance } from '@ascend-ai/sdk';
// Initialize ASCEND client
const ascend = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
agentId: 'mcp-database-server',
agentName: 'MCP Database Server'
});
// Create MCP server
const server = new Server({
name: "database-server",
version: "1.0.0"
});
// Define tools with governance
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "execute_sql",
description: "Execute SQL query",
inputSchema: {
type: "object",
properties: {
query: { type: "string" }
}
}
}
]
}));
// Governed tool handler
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
// Apply governance
const decision = await ascend.evaluateAction({
actionType: `mcp.${name}`,
resource: "database",
parameters: args
});
if (!decision.executionAllowed) {
return {
content: [{
type: "text",
text: `Tool execution denied: ${decision.reason}`
}],
isError: true
};
}
// Execute tool
const result = await executeSql(args.query);
// Log completion
await ascend.logActionCompleted(decision.actionId, { success: true });
return {
content: [{ type: "text", text: JSON.stringify(result) }]
};
});
Governance Middleware
Using mcpGovernance Wrapper
Wrap individual tools with governance:
// Source: sdk/nodejs/src/mcp.ts
import { mcpGovernance, requireGovernance, highRiskAction } from '@ascend-ai/sdk';
// Wrap a tool with governance
const governedTool = mcpGovernance({
actionType: 'database.query',
resource: 'production_db',
riskLevel: 'medium'
})(originalToolHandler);
// Mark as requiring governance
const protectedTool = requireGovernance({
actionType: 'database.delete',
riskLevel: 'high'
})(deleteHandler);
// High-risk action helper
const dangerousTool = highRiskAction(async (params) => {
return await performDangerousOperation(params);
});
Governance Options
interface GovernanceOptions {
actionType: string; // Action category
resource: string; // Resource being accessed
riskLevel?: 'low' | 'medium' | 'high' | 'critical';
requireApproval?: boolean; // Always require human approval
timeout?: number; // Decision timeout (ms)
context?: Record<string, any>; // Additional context
}
Python MCP Server
from mcp import Server
from ascend import AscendClient
# Initialize ASCEND
client = AscendClient(
api_key=os.environ["ASCEND_API_KEY"],
agent_id="mcp-python-server"
)
server = Server("python-server")
@server.tool("execute_sql")
async def execute_sql(query: str):
# Evaluate governance
from ascend import AgentAction
action = AgentAction(
agent_id="mcp-python-server",
agent_name="MCP Python Server",
action_type="mcp.execute_sql",
resource=f"Execute: {query[:50]}",
tool_name="postgresql",
action_details={"query": query}
)
result = client.submit_action(action)
if not result.is_approved():
raise PermissionError(f"Query denied: {result.reason}")
# Execute query
data = await run_query(query)
return {"result": data}
Tool Risk Classification
Default Risk Levels
| Tool Pattern | Risk Level | Approval |
|---|---|---|
*_read, *_list, *_get | LOW | Auto |
*_create, *_update | MEDIUM | Auto/Single |
*_delete, *_drop | HIGH | Multi |
*_execute, *_admin | CRITICAL | Escalation |
Custom Classification
const tools = {
// Low risk - auto-approve
read_file: mcpGovernance({
actionType: 'file.read',
riskLevel: 'low'
})(readFileHandler),
// High risk - requires approval
delete_all: mcpGovernance({
actionType: 'database.delete_all',
riskLevel: 'critical',
requireApproval: true
})(deleteAllHandler)
};
MCP Server Registry
Register your MCP server with ASCEND for centralized management:
// Register MCP server
await ascend.registerMcpServer({
serverId: 'database-server-001',
serverName: 'Production Database Server',
serverType: 'database',
tools: [
{ name: 'execute_sql', riskLevel: 'high' },
{ name: 'list_tables', riskLevel: 'low' }
],
environment: 'production'
});
Error Handling
import { AscendClient, AuthorizationError, TimeoutError } from '@ascend-ai/sdk';
server.setRequestHandler("tools/call", async (request) => {
try {
const decision = await ascend.evaluateAction({
actionType: `mcp.${request.params.name}`,
resource: "database"
});
if (!decision.executionAllowed) {
return {
content: [{
type: "text",
text: `Denied: ${decision.reason}`
}],
isError: true
};
}
// Execute tool...
} catch (error) {
if (error instanceof TimeoutError) {
return {
content: [{
type: "text",
text: "Governance timeout - please retry"
}],
isError: true
};
}
// Fail secure - deny on unknown errors
return {
content: [{
type: "text",
text: "Governance error - operation blocked"
}],
isError: true
};
}
});
Best Practices
1. Classify All Tools
// Map every tool to an action type
const toolActionTypes = {
'execute_sql': 'database.query',
'read_file': 'file.read',
'write_file': 'file.write',
'send_email': 'communication.email',
'make_payment': 'financial.payment'
};
2. Include Context
const decision = await ascend.evaluateAction({
actionType: 'database.query',
resource: 'production_db',
parameters: { query: sql },
context: {
user_id: sessionUserId,
session_id: sessionId,
mcp_server: 'database-server',
claude_model: 'claude-3-opus'
}
});
3. Log Completions
// Always log action outcomes for audit
try {
const result = await executeAction();
await ascend.logActionCompleted(decision.actionId, {
result: { success: true, rows: result.length }
});
} catch (error) {
await ascend.logActionFailed(decision.actionId, {
error: { message: error.message }
});
throw error;
}
Complete Example
// mcp-server.ts - Complete governed MCP server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { AscendClient, FailMode } from '@ascend-ai/sdk';
const ascend = new AscendClient({
apiKey: process.env.ASCEND_API_KEY!,
agentId: 'mcp-database-server',
agentName: 'MCP Database Server',
failMode: FailMode.CLOSED
});
const server = new Server({
name: "governed-database-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// Tool definitions
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "query",
description: "Execute read-only SQL query",
inputSchema: {
type: "object",
properties: {
sql: { type: "string", description: "SQL SELECT query" }
},
required: ["sql"]
}
},
{
name: "execute",
description: "Execute SQL statement (requires approval)",
inputSchema: {
type: "object",
properties: {
sql: { type: "string", description: "SQL statement" }
},
required: ["sql"]
}
}
]
}));
// Governed tool handler
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
// Determine risk level
const isReadOnly = name === 'query' && /^SELECT/i.test(args.sql);
const riskLevel = isReadOnly ? 'low' : 'high';
// Evaluate governance
const decision = await ascend.evaluateAction({
actionType: `mcp.database.${name}`,
resource: 'production_db',
parameters: { sql: args.sql },
riskIndicators: {
risk_level: riskLevel,
data_access: !isReadOnly
}
});
if (!decision.executionAllowed) {
return {
content: [{
type: "text",
text: `Operation denied by ASCEND governance.\n` +
`Reason: ${decision.reason}\n` +
`Risk Level: ${decision.riskLevel}`
}],
isError: true
};
}
// Execute the query
try {
const result = await executeQuery(args.sql);
await ascend.logActionCompleted(decision.actionId, {
result: { rowCount: result.length }
});
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
await ascend.logActionFailed(decision.actionId, {
error: { message: error.message }
});
throw error;
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
Troubleshooting
Tool calls not being intercepted by governance
Cause: The mcpGovernance wrapper is not applied to the tool handler, or the MCP server was initialized before the ASCEND client was configured.
Solution: Ensure the ASCEND client is initialized before registering tools. Wrap each tool handler: const governed = mcpGovernance({ actionType: 'db.query' })(handler). Verify the client has a valid API key and can reach the ASCEND API.
All tool calls denied with GOVERNANCE_DENIED
Cause: The Cedar policy engine is returning DENY for all evaluations. This can happen when no matching ALLOW policy exists, as the default decision is DENY.
Solution: Check your governance policies in the ASCEND dashboard under Governance > Policies. Ensure at least one policy matches your tool's action type and resource. For testing, you can temporarily set failMode: 'open' (not recommended for production).
MCP server crashes on startup with ASCEND initialization error
Cause: The ASCEND API key is missing or the API endpoint is unreachable during server initialization.
Solution: Set ASCEND_API_KEY in your environment before starting the MCP server. Verify connectivity: curl https://pilot.owkai.app/health. If using Docker, ensure the environment variable is passed to the container.
Next Steps
- LangChain Integration — Govern LangChain tools
- Custom Agents — Build governed agents
- Smart Rules — Create MCP-specific rules
Document Version: 2026.04 | Last Updated: April 2026