Skip to content

Instantly share code, notes, and snippets.

@laiso
Created April 11, 2025 11:17
Show Gist options
  • Save laiso/d82d4320f2277627ed1ca8060ef2860e to your computer and use it in GitHub Desktop.
Save laiso/d82d4320f2277627ed1ca8060ef2860e to your computer and use it in GitHub Desktop.
SETUP: docker build -t mcp-server-fetch:latest .
FROM denoland/deno:latest
# Create the application directory
WORKDIR /app
# Copy dependency files first for caching
COPY deno.json deno.lock* ./
# Copy the rest of the application code
COPY . .
# Cache application dependencies (optional)
RUN deno cache mcp-server-fetch.ts
# Expose port 8000 (if needed)
EXPOSE 8000
# Run the application
CMD ["run", "--allow-net", "--allow-read", "--allow-env", "mcp-server-fetch.ts"]
#!/usr/bin/env -S deno run --allow-net --allow-read --allow-env
/**
* Simple MCP Server for Deno with Fetch Tool
* Using Model Context Protocol SDK
*/
import { Server } from "npm:@modelcontextprotocol/[email protected]/server/index.js";
import { StdioServerTransport } from "npm:@modelcontextprotocol/[email protected]/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "npm:@modelcontextprotocol/[email protected]/types.js";
// Fetch tool definition
const FETCH_TOOL: Tool = {
name: "fetch_url",
description: "Fetches content from the specified URL",
inputSchema: {
type: "object",
properties: {
url: {
type: "string",
description: "URL to fetch content from"
},
headers: {
type: "object",
additionalProperties: { type: "string" },
description: "Optional headers for the request"
}
},
required: ["url"]
}
};
// Server implementation
const server = new Server(
{
name: "Fetch-MCP-Server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
},
);
function isFetchArgs(args: unknown): args is { url: string; headers?: Record<string, string> } {
return (
typeof args === "object" &&
args !== null &&
"url" in args &&
typeof (args as { url: string }).url === "string"
);
}
// Tool execution function
async function fetchExecute(url: string, headers?: Record<string, string>) {
try {
const response = await fetch(url, {
headers: headers || {},
});
if (!response.ok) {
throw new Error(`Fetch failed with status: ${response.status}`);
}
const contentType = response.headers.get("content-type") || "";
// Handle response based on content type
if (contentType.includes("application/json")) {
const jsonData = await response.json();
return JSON.stringify(jsonData, null, 2);
} else {
const textData = await response.text();
return textData;
}
} catch (error: unknown) {
console.error("Error in fetch:", error);
const errorMessage = error instanceof Error
? error.message
: "An unknown error occurred";
return `Error fetching content: ${errorMessage}`;
}
}
// Tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [FETCH_TOOL],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
if (!args) {
throw new Error("No arguments provided");
}
switch (name) {
case "fetch_url": {
if (!isFetchArgs(args)) {
throw new Error("Invalid arguments for fetch_url");
}
const { url, headers } = args;
const results = await fetchExecute(url, headers);
return {
content: [{ type: "text", text: results }],
isError: false,
};
}
default:
return {
content: [{ type: "text", text: `Unknown tool: ${name}` }],
isError: true,
};
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Fetch MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
Deno.exit(1);
});
{
"servers": {
"deno-fetch": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"mcp-server-fetch:latest"
]
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment