1. Conduid
  2. Developer Tools
  3. In A Box
MCP server · Developer Tools

In A Box

MCP server: In A Box

Unclaimed last commit 9 months ago productivity
41Fair

Scored 2 days ago · breakdown

About In A Box

In A Box is an MCP server published by pavlinhristov in the Developer Tools category: mCP server: In A Box. It has been installed 0 times through Conduid.

The repository has 7 stars and 3 forks, with the last commit 9 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues (0) before depending on it in production.

Install

Install
npx mcp-in-a-box

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

Ask AI about In A Box

Powered by Claude · Grounded in docs

I know everything about In A Box. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

  • ·README presentNot checked yet.
  • ·License declaredNot checked yet.
  • ·Tests presentNot checked yet.
  • ·Dependencies pinnedNot checked yet.
  • ·No dynamic code executionNot checked yet.
  • !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.

README

MCP in a Box 📦

Build Your First Custom MCP Server in Under 30 Minutes

A hands-on lab for learning the Model Context Protocol (MCP) by building a working weather dashboard server that connects to OpenAI's API.


What You'll Build

A fully functional MCP server that:

  • Exposes a get_weather tool that returns mock weather data
  • Works with OpenAI's Responses API
  • Can be extended to connect to real APIs

By the end, you'll understand how MCP servers work and how to build your own.


Prerequisites

Knowledge Required

  • Basic JavaScript/Node.js (variables, functions, async/await)
  • Comfortable using the terminal/command line
  • Basic understanding of APIs (what they are, how requests work)

Tools Required

  • Node.js v18 or higher (download)
  • npm (comes with Node.js)
  • A code editor (VS Code recommended)
  • ngrok for tunneling (free tier works) (download)
  • OpenAI API key (for testing with Responses API)

Check Your Setup

Run these commands to verify you're ready:

node --version    # Should show v18.x.x or higher
npm --version     # Should show 9.x.x or higher

Lab Overview

Step What You'll Do Time
1 Set up the project 5 min
2 Build the MCP server 10 min
3 Test locally 5 min
4 Connect to OpenAI 10 min

Total time: ~30 minutes


Step 1: Set Up the Project

1.1 Create Your Project Directory

mkdir mcp-weather-server
cd mcp-weather-server

1.2 Initialize the Project

npm init -y

1.3 Configure for ES Modules

Open package.json and replace its contents with:

{
  "name": "mcp-weather-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.20.2",
    "zod": "^3.25.76"
  }
}

1.4 Install Dependencies

npm install

You should see the packages install successfully.


Step 2: Build the MCP Server

2.1 Understanding the Architecture

Before we code, here's what we're building:

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  OpenAI API     │────▶│  Your MCP Server │────▶│  Weather Data   │
│  (or ChatGPT)   │◀────│  (Node.js)       │◀────│  (mock/real)    │
└─────────────────┘     └──────────────────┘     └─────────────────┘

Key concepts:

  • Tools: Functions your server exposes (like get_weather)
  • Input Schema: What parameters the tool accepts (validated with Zod)
  • Transport: How requests/responses flow (we use Streamable HTTP)

2.2 Create the Server File

Create a new file called server.js and paste the following code:

// server.js - MCP Weather Server
// A simple MCP server that provides weather information

import { createServer } from "node:http";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";

// ============================================
// WEATHER DATA (Mock data for the lab)
// In production, replace with real API calls
// ============================================

const weatherDatabase = {
  "new york": { temp: 72, condition: "Partly Cloudy", humidity: 65, wind: 8 },
  "los angeles": { temp: 78, condition: "Sunny", humidity: 45, wind: 5 },
  "chicago": { temp: 68, condition: "Windy", humidity: 55, wind: 15 },
  "miami": { temp: 85, condition: "Humid", humidity: 80, wind: 10 },
  "seattle": { temp: 62, condition: "Rainy", humidity: 75, wind: 7 },
  "denver": { temp: 70, condition: "Clear", humidity: 30, wind: 6 },
  "boston": { temp: 65, condition: "Cloudy", humidity: 60, wind: 12 },
  "san francisco": { temp: 64, condition: "Foggy", humidity: 70, wind: 9 },
};

// Helper function to get weather data
function getWeatherData(city) {
  const normalizedCity = city.toLowerCase().trim();
  const weather = weatherDatabase[normalizedCity];
  
  if (!weather) {
    return {
      found: false,
      city: city,
      message: `Weather data not available for "${city}". Available cities: ${Object.keys(weatherDatabase).join(", ")}`,
    };
  }
  
  return {
    found: true,
    city: city,
    temperature: weather.temp,
    temperatureUnit: "°F",
    condition: weather.condition,
    humidity: weather.humidity,
    humidityUnit: "%",
    windSpeed: weather.wind,
    windUnit: "mph",
    timestamp: new Date().toISOString(),
  };
}

// ============================================
// MCP SERVER SETUP
// ============================================

function createWeatherServer() {
  // Initialize the MCP server with name and version
  const server = new McpServer({
    name: "weather-server",
    version: "1.0.0",
  });

  // ----------------------------------------
  // TOOL: get_weather
  // This is the main tool our server exposes
  // ----------------------------------------
  server.registerTool(
    "get_weather",  // Tool name (what the model calls)
    {
      title: "Get Weather",
      description: "Get current weather information for a city. Returns temperature, conditions, humidity, and wind speed.",
      
      // Input schema using Zod for validation
      inputSchema: {
        city: z.string().min(1).describe("The city name to get weather for"),
      },
      
      // Metadata for OpenAI integration
      _meta: {
        "openai/toolInvocation/invoking": "Checking weather...",
        "openai/toolInvocation/invoked": "Weather retrieved!",
      },
    },
    
    // The handler function - runs when the tool is called
    async ({ city }) => {
      console.log(`[get_weather] Request for city: ${city}`);
      
      const weatherData = getWeatherData(city);
      
      if (!weatherData.found) {
        return {
          structuredContent: weatherData,
          content: [{ type: "text", text: weatherData.message }],
        };
      }
      
      // Return structured data for the model to use
      return {
        structuredContent: weatherData,
        content: [
          {
            type: "text",
            text: `Weather in ${city}: ${weatherData.temperature}${weatherData.temperatureUnit}, ${weatherData.condition}`,
          },
        ],
      };
    }
  );

  // ----------------------------------------
  // TOOL: list_cities
  // Bonus tool to show available cities
  // ----------------------------------------
  server.registerTool(
    "list_cities",
    {
      title: "List Available Cities",
      description: "Get a list of all cities that have weather data available.",
      inputSchema: {},  // No input required
    },
    async () => {
      const cities = Object.keys(weatherDatabase);
      return {
        structuredContent: { cities, count: cities.length },
        content: [
          {
            type: "text",
            text: `Available cities: ${cities.join(", ")}`,
          },
        ],
      };
    }
  );

  return server;
}

// ============================================
// HTTP SERVER
// Handles incoming requests and routes to MCP
// ============================================

const PORT = process.env.PORT || 3000;
const MCP_PATH = "/mcp";

const httpServer = createServer(async (req, res) => {
  const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
  
  // Log all incoming requests
  console.log(`[${new Date().toISOString()}] ${req.method} ${url.pathname}`);

  // Handle CORS preflight requests
  if (req.method === "OPTIONS") {
    res.writeHead(204, {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "POST, GET, DELETE, OPTIONS",
      "Access-Control-Allow-Headers": "content-type, mcp-session-id",
      "Access-Control-Expose-Headers": "Mcp-Session-Id",
    });
    res.end();
    return;
  }

  // Health check endpoint
  if (req.method === "GET" && url.pathname === "/") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
      status: "ok",
      server: "MCP Weather Server",
      version: "1.0.0",
      mcpEndpoint: MCP_PATH,
    }));
    return;
  }

  // MCP endpoint - handle all MCP requests
  const MCP_METHODS = new Set(["POST", "GET", "DELETE"]);
  if (url.pathname === MCP_PATH && MCP_METHODS.has(req.method || "")) {
    // Set CORS headers
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");

    // Create a new server instance for this request
    const server = createWeatherServer();
    
    // Create the transport layer
    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: undefined,  // Stateless mode
      enableJsonResponse: true,
    });

    // Clean up when connection closes
    res.on("close", () => {
      transport.close();
      server.close();
    });

    try {
      await server.connect(transport);
      await transport.handleRequest(req, res);
    } catch (error) {
      console.error("[MCP Error]", error);
      if (!res.headersSent) {
        res.writeHead(500, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ error: "Internal server error" }));
      }
    }
    return;
  }

  // 404 for unknown routes
  res.writeHead(404, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ error: "Not found" }));
});

// Start the server
httpServer.listen(PORT, () => {
  console.log(`
╔════════════════════════════════════════════════╗
║     MCP Weather Server is Running! 🌤️          ║
╠════════════════════════════════════════════════╣
║  Local:    http://localhost:${PORT}              ║
║  MCP:      http://localhost:${PORT}${MCP_PATH}            ║
╠════════════════════════════════════════════════╣
║  Next steps:                                   ║
║  1. Test: curl http://localhost:${PORT}          ║
║  2. Tunnel: ngrok http ${PORT}                   ║
║  3. Connect to OpenAI Responses API            ║
╚════════════════════════════════════════════════╝
  `);
});

2.3 Understanding the Code

Let's break down the key parts:

1. Tool Registration

server.registerTool(
  "get_weather",           // Name the model uses to call it
  {
    title: "Get Weather",  // Human-readable title
    description: "...",    // Helps the model know when to use it
    inputSchema: { ... },  // Zod schema for validation
  },
  async ({ city }) => { }  // Handler function
);

2. Response Structure

return {
  structuredContent: { ... },  // JSON data the model can reason about
  content: [{ type: "text", text: "..." }],  // Human-readable response
};

3. HTTP Transport The server uses StreamableHTTPServerTransport which is the recommended transport for production MCP servers.


Step 3: Test Locally

3.1 Start the Server

npm start

You should see:

╔════════════════════════════════════════════════╗
║     MCP Weather Server is Running! 🌤️          ║
...

3.2 Test the Health Endpoint

Open a new terminal and run:

curl http://localhost:3000

Expected response:

{"status":"ok","server":"MCP Weather Server","version":"1.0.0","mcpEndpoint":"/mcp"}

3.3 Test with MCP Inspector (Optional but Recommended)

The MCP Inspector lets you test your tools interactively:

npx @modelcontextprotocol/inspector@latest http://localhost:3000/mcp

This opens a browser window where you can:

  • See your registered tools
  • Call get_weather with different cities
  • View the responses

Step 4: Connect to OpenAI

4.1 Expose Your Server with ngrok

In a new terminal (keep your server running):

ngrok http 3000

ngrok will show you a public URL like:

Forwarding  https://abc123.ngrok.app -> http://localhost:3000

Copy the HTTPS URL (e.g., https://abc123.ngrok.app)

4.2 Test via OpenAI Responses API

Create a file called test-openai.sh:

#!/bin/bash

# Replace with your values
OPENAI_API_KEY="your-api-key-here"
MCP_SERVER_URL="https://your-ngrok-url.ngrok.app/mcp"

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4.1",
    "tools": [
      {
        "type": "mcp",
        "server_label": "weather",
        "server_url": "'"$MCP_SERVER_URL"'",
        "require_approval": "never"
      }
    ],
    "input": "What is the weather like in Seattle?"
  }'

Run it:

chmod +x test-openai.sh
./test-openai.sh

4.3 Expected Response

The API will:

  1. Connect to your MCP server
  2. Discover the get_weather tool
  3. Call it with city: "Seattle"
  4. Return the weather data in the response

You should see your server log:

[get_weather] Request for city: Seattle

Challenges: Extend Your Server

Now that you have a working MCP server, try these challenges:

Challenge 1: Add a Forecast Tool (Easy)

Add a get_forecast tool that returns a 3-day forecast.

Create mock forecast data and register a new tool:

server.registerTool("get_forecast", { ... }, async ({ city }) => { ... });

Challenge 2: Connect to a Real Weather API (Medium)

Replace the mock data with calls to a real weather API like OpenWeatherMap.

  1. Sign up for a free API key
  2. Use fetch() to call the API
  3. Transform the response to your structured format

Challenge 3: Add Temperature Unit Conversion (Easy)

Add a parameter to return temperature in Celsius or Fahrenheit.

Update the input schema:

inputSchema: {
  city: z.string(),
  unit: z.enum(["celsius", "fahrenheit"]).optional().default("fahrenheit"),
}

Troubleshooting

Server Won't Start

Error: Cannot find module

  • Run npm install to install dependencies
  • Check that package.json has "type": "module"

Error: Port already in use

  • Change the port: PORT=3001 npm start
  • Or kill the process using port 3000

ngrok Issues

ngrok not found

  • Install ngrok: npm install -g ngrok or download from ngrok.com
  • Run ngrok authtoken YOUR_TOKEN if required

Tunnel disconnects

  • Free tier tunnels expire. Just restart ngrok.

OpenAI API Errors

401 Unauthorized

  • Check your API key is correct
  • Ensure you have Responses API access

MCP connection failed

  • Verify ngrok is running
  • Check the MCP endpoint URL ends with /mcp
  • Look at your server logs for errors

What You Learned

✅ How MCP servers are structured
✅ How to register tools with input schemas
✅ How to return structured content
✅ How to expose your server for external access
✅ How to connect to OpenAI's Responses API


Next Steps

  1. Read OpenAI's MCP Documentation: https://developers.openai.com/apps-sdk/build/mcp-server/
  2. Explore the MCP Specification: https://modelcontextprotocol.io/
  3. Build a ChatGPT App: Add a UI component using the Apps SDK
  4. Add Authentication: Implement OAuth for user-specific data

Resources


Congratulations! 🎉 You've built your first MCP server. Now go build something amazing!

README mirrored from the source repository 2 days ago. The original is authoritative.

Questions

About In A Box

How do I install In A Box?

Run npx mcp-in-a-box, then add the server to your MCP client's configuration. Conduid has recorded 0 installs, so the command is known to work with current clients.

Is In A Box safe to use with an AI agent?

Its trust score is 41 out of 100 (fair). It passes 0 of 1 static security checks; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is In A Box still maintained?

The last commit was 9 months ago, with 0 open issues. That's long enough that you should check whether the maintainer is responding to issues before depending on it.