Unlocking MCP Resources: Why Your Copilot Studio Agent Isn’t Using Its Tools and How to Fix It

Listen to this Post

Featured Image

Introduction:

Many developers building with Microsoft Copilot Studio encounter a perplexing issue: their MCP (Model Context Protocol) resources appear in the UI but remain stubbornly unused by the AI agent. This breakdown occurs due to a critical implementation detail often overlooked—resources are only utilized when correctly returned as outputs from MCP tools. Understanding this architectural requirement is essential for building fully functional AI assistants.

Learning Objectives:

  • Understand the fundamental requirement for MCP resource utilization in Copilot Studio
  • Learn to construct a compliant MCP server that properly returns resource outputs
  • Master the end-to-end integration between custom MCP tools and Copilot Studio agents

You Should Know:

1. MCP Server Foundation with Environment Configuration

// server.ts - Basic MCP server setup
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { 
CallToolRequest, 
ListToolsRequest 
} from '@modelcontextprotocol/sdk/types.js';

const server = new Server({
name: 'custom-mcp-server',
version: '1.0.0',
}, {
capabilities: {
tools: {}
}
});

This TypeScript code establishes the foundation of an MCP server. The Server class from the Model Context Protocol SDK creates the core infrastructure, while the StdioServerTransport handles communication. The capabilities object declares what functionalities your server provides—in this case, custom tools that Copilot Studio can invoke.

2. Resource Tool Implementation with Proper Output Structure

// resource-tool.ts - Critical resource return implementation
async function handleResourceTool(request: CallToolRequest) {
const { arguments: { resourceId, query } } = request;

// Fetch or process your resource data
const resourceData = await fetchResourceContent(resourceId, query);

// CORRECT: Returning resource as tool output
return {
content: [
{
type: "resource",
resource: {
id: resourceId,
content: {
type: "text",
text: resourceData
}
}
}
]
};
}

// Register the tool with the MCP server
server.setRequestHandler(ListToolsRequest, async () => ({
tools: [
{
name: "get_company_resource",
description: "Retrieves and returns company knowledge base articles",
inputSchema: {
type: "object",
properties: {
resourceId: { type: "string" },
query: { type: "string" }
},
required: ["resourceId"]
}
}
]
}));

This implementation demonstrates the crucial pattern: resources must be explicitly returned in the tool’s output content array. The `type: “resource”` declaration signals to Copilot Studio that this output should be treated as a consumable resource rather than just display text.

3. MCP Server Authentication and Security Headers

 mcp-server.env - Environment configuration
MCP_SERVER_PORT=8080
MCP_API_KEY=cs_sk_1234567890abcdef
RESOURCE_BASE_URL=https://api.company-resources.com/v1
CORS_ORIGINS=https://copilotstudio.microsoft.com

Docker security configuration
docker run -p 8080:8080 \
-e MCP_API_KEY=$MCP_API_KEY \
-e RESOURCE_BASE_URL=$RESOURCE_BASE_URL \
--security-opt=no-new-privileges:true \
custom-mcp-server:latest

Proper environment configuration ensures your MCP server can securely communicate with Copilot Studio. The API key authentication prevents unauthorized access, while CORS settings explicitly allow requests from the Copilot Studio domain. The Docker security options harden the container runtime.

4. Resource Caching Implementation for Performance

// caching-layer.ts - Performance optimization
import NodeCache from 'node-cache';

const resourceCache = new NodeCache({ 
stdTTL: 300, // 5 minutes
checkperiod: 60 
});

async function getCachedResource(resourceId: string, query: string) {
const cacheKey = <code>${resourceId}:${query}</code>;
let resource = resourceCache.get(cacheKey);

if (!resource) {
resource = await fetchFreshResource(resourceId, query);
resourceCache.set(cacheKey, resource);
console.log(<code>Cache miss for ${cacheKey}</code>);
} else {
console.log(<code>Cache hit for ${cacheKey}</code>);
}

return resource;
}

This caching layer significantly improves response times for frequently accessed resources. The 5-minute TTL balances data freshness with performance, while the checkperiod ensures expired entries are regularly cleaned up. Cache hits and misses are logged for monitoring.

5. Error Handling and Fallback Mechanisms

// error-handling.ts - Robust error management
async function handleResourceToolWithFallback(request: CallToolRequest) {
try {
const result = await handleResourceTool(request);
return result;
} catch (error) {
console.error(<code>Resource tool failed: ${error.message}</code>);

// Fallback to default response
return {
content: [
{
type: "text",
text: "I'm unable to access that resource currently. Please try again later or contact support if the issue persists."
}
]
};
}
}

// Validation function
function validateResourceRequest(args: any) {
if (!args.resourceId || typeof args.resourceId !== 'string') {
throw new Error("Invalid resourceId parameter");
}
if (args.resourceId.length > 100) {
throw new Error("Resource ID too long");
}
}

Comprehensive error handling ensures your MCP server remains stable even when underlying resources are unavailable. The validation function prevents malformed requests from causing server crashes, while the fallback mechanism maintains user experience during outages.

6. Docker Containerization for Deployment

 Dockerfile - Production-ready container
FROM node:18-alpine

WORKDIR /app
COPY package.json ./
RUN npm ci --only=production

COPY dist/ ./dist/
COPY node_modules/ ./node_modules/

USER node:node
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1

CMD ["node", "dist/server.js"]

This Dockerfile creates a secure, production-ready container. The Alpine Linux base image minimizes attack surface, while the non-root user enhances security. The health check enables container orchestration systems to monitor service availability automatically.

7. Testing and Validation Commands

 test-mcp-connection.sh - Validation script
!/bin/bash

Test MCP server health
curl -H "Authorization: Bearer $MCP_API_KEY" \
http://localhost:8080/health

Test tool listing
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer $MCP_API_KEY" \
-d '{"method": "tools/list"}' \
http://localhost:8080/mcp

Test resource tool invocation
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer $MCP_API_KEY" \
-d '{
"method": "tools/call",
"params": {
"name": "get_company_resource",
"arguments": {
"resourceId": "hr-policy-2024",
"query": "vacation time"
}
}
}' \
http://localhost:8080/mcp

These validation commands allow you to test your MCP server independently of Copilot Studio. The health check verifies server availability, while the tool listing and invocation tests ensure proper functionality before integration.

What Undercode Say:

  • The critical breakthrough is understanding that MCP resources must be explicitly returned in tool outputs—mere visibility in the UI doesn’t guarantee usage
  • Proper server implementation requires both correct architectural patterns and comprehensive error handling for production reliability
  • Containerization and caching are not optional optimizations but essential components for enterprise-grade MCP servers

The architectural insight that resolved the “visible but unused” resource problem represents a fundamental shift in how developers must approach MCP integration. Rather than treating resources as automatically available context, they must be strategically returned from tool invocations. This pattern enables more precise control over resource utilization but requires careful implementation. The sample provided demonstrates that successful integration hinges on combining correct protocol adherence with production-ready operational practices including security, monitoring, and performance optimization.

Prediction:

The resolution of this MCP resource utilization challenge will accelerate enterprise adoption of Copilot Studio by providing clearer implementation patterns. As more organizations successfully integrate custom knowledge bases and tools, we’ll see a proliferation of specialized AI assistants across departments from HR to IT support. This pattern will become the standard approach for contextual AI tool integration, eventually expanding beyond Microsoft’s ecosystem to influence how other AI platforms handle external resource integration. The clear documentation and working samples will reduce implementation time from weeks to days, dramatically increasing ROI on AI assistant investments.

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Adilei Using – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky