Skip to main content

MCP Server Governance

FieldValue
Document IDASCEND-SDK-003
Version2026.04
Last UpdatedApril 2026
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 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.

Version Pinning

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 PatternRisk LevelApproval
*_read, *_list, *_getLOWAuto
*_create, *_updateMEDIUMAuto/Single
*_delete, *_dropHIGHMulti
*_execute, *_adminCRITICALEscalation

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


Document Version: 2026.04 | Last Updated: April 2026