Command Palette

Search for a command to run...

UnylyUnyly
Browse all

EmbedMCP

FreeNot checked

Provides easy-to-use kits that allow you to quickly create MCP servers Any embedded devices

GitHubEmbed

About

Provides easy-to-use kits that allow you to quickly create MCP servers Any embedded devices

README

A lightweight C library for creating MCP (Model Context Protocol) servers that transforms your existing C functions into AI-accessible tools with minimal code changes.

License: MIT C Standard Platform MCP

English简体中文

Why EmbedMCP?

EmbedMCP bridges the gap between your existing C codebase and modern AI systems. Instead of rewriting your battle-tested C functions, EmbedMCP lets you expose them to AI models through the standardized Model Context Protocol (MCP) with minimal code changes.

Key Features

  • 🚀 Simple Integration: Copy one folder, include one header file
  • ⚡ High Performance: Direct C function calls with minimal overhead
  • 🔧 Cross-Platform: Runs on 15+ platforms via Universal HAL
  • 📦 Zero Dependencies: Self-contained library with no external requirements
  • 🎯 Two Registration Methods: Magic macros for simple functions, full control for complex ones
  • 🌐 Multiple Transports: Streamable HTTP and STDIO support for different use cases
  • 🧠 Smart Memory Management: Automatic cleanup with clear ownership rules
  • 📊 Array Support: Handle both simple parameters and complex data structures

Quick Start

Installation

  1. Download EmbedMCP

    git clone https://github.com/AaronWander/EmbedMCP.git
    cd EmbedMCP
    
  2. Copy to your project

    cp -r embed_mcp/ your_project/
    

Basic Usage

#include "embed_mcp/embed_mcp.h"

// Your business function
double add_numbers(double a, double b) {
    return a + b;
}

// Generate wrapper with macro
EMBED_MCP_WRAPPER(add_wrapper, add_numbers, DOUBLE, DOUBLE, a, DOUBLE, b)

int main() {
    embed_mcp_config_t config = {
        .name = "MathServer",
        .version = "1.0.0",
        .instructions = "Simple math operations server",
        .port = 8080
    };

    embed_mcp_server_t *server = embed_mcp_create(&config);

    // Register function
    const char* names[] = {"a", "b"};
    const char* descs[] = {"First number", "Second number"};
    mcp_param_type_t types[] = {MCP_PARAM_DOUBLE, MCP_PARAM_DOUBLE};

    embed_mcp_add_tool(server, "add", "Add two numbers",
                       names, descs, types, 2, MCP_RETURN_DOUBLE, add_wrapper, NULL);

    embed_mcp_run(server, EMBED_MCP_TRANSPORT_HTTP);
    embed_mcp_destroy(server);
    return 0;
}

Build and Run

# Build
make

# Run Streamable HTTP server
./bin/mcp_server --transport http --port 8080

# Or run STDIO server
./bin/mcp_server --transport stdio

Function Registration

EmbedMCP supports two registration approaches:

Strict Parameter Access (Recommended for robust validation)

In addition to get_* helpers, you can use strict try_get_* accessors to distinguish between missing/invalid input and real zero/empty values.

int64_t user_id;
if (!params->try_get_int(params, "user_id", &user_id)) {
    // handle missing or invalid type
}

double* values = NULL;
size_t count = 0;
if (params->try_get_double_array(params, "values", &values, &count)) {
    // use values, then free(values)
}

Simple Functions (Recommended)

// Business function
double add_numbers(double a, double b) {
    return a + b;
}

// One-line wrapper generation
EMBED_MCP_WRAPPER(add_wrapper, add_numbers, DOUBLE, DOUBLE, a, DOUBLE, b)

// Register
const char* names[] = {"a", "b"};
const char* descs[] = {"First number", "Second number"};
mcp_param_type_t types[] = {MCP_PARAM_DOUBLE, MCP_PARAM_DOUBLE};

embed_mcp_add_tool(server, "add", "Add two numbers",
                   names, descs, types, 2, MCP_RETURN_DOUBLE, add_wrapper, NULL);

Array Functions (Advanced)

// Business function
double sum_numbers(double* numbers, size_t count) {
    double sum = 0.0;
    for (size_t i = 0; i < count; i++) {
        sum += numbers[i];
    }
    return sum;
}

// Manual wrapper (handles memory management)
void* sum_wrapper(mcp_param_accessor_t* params, void* user_data) {
    size_t count;
    double* numbers = params->get_double_array(params, "numbers", &count);

    double result_val = sum_numbers(numbers, count);
    free(numbers); // Clean up

    double* result = malloc(sizeof(double));
    *result = result_val;
    return result;
}

// Register with array parameter
mcp_param_desc_t params[] = {
    MCP_PARAM_ARRAY_DOUBLE_DEF("numbers", "Array of numbers", "A number", 1)
};

embed_mcp_add_tool(server, "sum", "Sum numbers", params, NULL, NULL, 1,
                   MCP_RETURN_DOUBLE, sum_wrapper, NULL);

Complex Nested Input (Schema-Based)

Use embed_mcp_add_tool_with_schema when your tool needs nested objects, arrays of objects, or strict schema constraints.

cJSON* submit_order_with_schema(const cJSON *args) {
    const cJSON *customer = cJSON_GetObjectItem(args, "customer");
    const cJSON *name = customer ? cJSON_GetObjectItem(customer, "name") : NULL;
    const cJSON *items = cJSON_GetObjectItem(args, "items");

    cJSON *result = cJSON_CreateObject();
    cJSON_AddStringToObject(result, "status", "accepted");
    cJSON_AddStringToObject(result, "customer",
        (name && cJSON_IsString(name)) ? cJSON_GetStringValue(name) : "unknown");
    cJSON_AddNumberToObject(result, "itemCount", cJSON_IsArray(items) ? cJSON_GetArraySize(items) : 0);
    return result;
}

const char *schema_json =
    "{\"type\":\"object\",\"properties\":{"
      "\"customer\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"}},\"required\":[\"name\"],\"additionalProperties\":false},"
      "\"items\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"sku\":{\"type\":\"string\"},\"qty\":{\"type\":\"integer\"}},\"required\":[\"sku\",\"qty\"],\"additionalProperties\":false}}"
    "},\"required\":[\"customer\",\"items\"],\"additionalProperties\":false}";

cJSON *schema = cJSON_Parse(schema_json);
embed_mcp_add_tool_with_schema(server, "submit_order", "Submit nested order payload", schema, submit_order_with_schema);
cJSON_Delete(schema);

Memory Management

EmbedMCP handles most memory management automatically:

  • Parameters: All input parameters are automatically freed after your function returns
  • JSON processing: Request/response parsing and cleanup is handled internally
  • Arrays: Dynamic arrays are automatically allocated and freed
  • Error handling: Memory is properly cleaned up even when errors occur

Your responsibility: String return values must use malloc():

char* get_weather(const char* city) {
    char* result = malloc(200);  // ✅ EmbedMCP will call free()
    sprintf(result, "Weather for %s: Sunny", city);
    return result;
}

Server Modes

Streamable HTTP Transport (Example)

./my_server --transport http --port 8080
  • Multiple concurrent clients
  • Session management with MCP-Session-Id headers
  • Protocol version negotiation via MCP-Protocol-Version headers
  • Web application backends
  • Development and testing

STDIO Transport

For MCP clients like Claude Desktop:

./my_server --transport stdio --quiet
  • Claude Desktop integration
  • AI assistant tools
  • Command-line workflows
  • Single client communication
  • --quiet suppresses business debug logs to keep stdio output cleaner for protocol tooling

🔧 Parameter Definition Macros

Powerful macros for complex parameter definitions

📊 Array Parameters

// Double array
MCP_PARAM_ARRAY_DOUBLE_DEF(
    "numbers",
    "Array of numbers",
    "A numeric value",
    1  // required
)

// String array
MCP_PARAM_ARRAY_STRING_DEF(
    "items",
    "List of items",
    "An item name",
    1  // required
)

// Bool array
MCP_PARAM_ARRAY_BOOL_DEF(
    "flags",
    "List of boolean flags",
    "A boolean value",
    0  // optional
)

🎯 Simple Parameters

// Double parameter
MCP_PARAM_DOUBLE_DEF(
    "temperature",
    "Temperature in Celsius",
    1  // required
)

// String parameter
MCP_PARAM_STRING_DEF(
    "city",
    "City name",
    0  // optional
)

Example Server

Validation errors now include clearer field-level details (for example: missing required field, unexpected field, or invalid nested field type).

Smoke regression is available via:

make test-smoke

The included example demonstrates all EmbedMCP features:

# Build and run example
make && ./bin/mcp_server --transport stdio

Available Demo Tools

Tool Parameters Description Example
add a: number, b: number Add two numbers add(10, 20)30
sum_numbers numbers: number[] Sum array of numbers sum_numbers([1,2,3])6
join_strings strings: string[], separator: string Join string array join_strings(["a","b"], ",")"a,b"
weather city: string Get weather info weather("济南") → Weather report
calculate_score base_points: int, grade: string, multiplier: number Calculate score with bonus calculate_score(80, "A", 1.2)120
submit_order customer: object, items: object[], priority?: int Schema-based nested payload example submit_order({...}) → accepted result

Testing with MCP Inspector

  1. Start the server: ./bin/mcp_server --transport http --port 8080
  2. Open MCP Inspector
  3. Connect to: http://localhost:8080/mcp
  4. Test the available tools

Platform Support

EmbedMCP is designed for maximum portability across embedded systems:

Embedded Systems

  • RTOS: FreeRTOS, Zephyr, ThreadX, embOS
  • MCUs: STM32, ESP32, Nordic nRF series
  • SBCs: Raspberry Pi, BeagleBone, Orange Pi

Requirements

  • Minimum: C99 compiler, 64KB RAM, 100KB flash
  • Recommended: 512KB RAM for complex applications
  • Dependencies: None (self-contained)

Use Cases

Industrial IoT

  • Sensor data processing: Expose C sensor drivers to AI models
  • Equipment monitoring: Real-time analysis of machine data
  • Predictive maintenance: AI-driven failure prediction

Embedded AI

  • Edge computing: Run AI inference on embedded devices
  • Smart devices: Voice assistants, smart cameras, IoT hubs
  • Robotics: AI-controlled robotic systems

Troubleshooting

Common Issues

Build errors:

# Missing dependencies
make deps

# Clean build
make clean && make

Runtime errors:

# Enable debug logging
./bin/mcp_server --transport stdio --debug

# Check memory usage
valgrind ./bin/mcp_server --transport stdio

Connection issues:

  • Ensure correct transport mode (Streamable HTTP vs STDIO)
  • Check firewall settings for Streamable HTTP mode
  • Verify MCP client configuration and protocol version headers

Contributing

At this time, we’re not accepting external code contributions (PRs).

Issues are welcome for bug reports and feature requests.

Development Setup

# Clone repository
git clone https://github.com/AaronWander/EmbedMCP.git
cd EmbedMCP

# Build debug version
make debug

# Run tests
make test

License

This project is licensed under the MIT License - see the LICENSE file for details.

👥 Community & Support

Community Resources

  • Contributing: At this time, we’re not accepting external code contributions (PRs).

  • Issues are welcome for bug reports and feature requests.

  • 🐛 Report bugs via GitHub Issues

  • 💡 Suggest features in Discussions

  • 💬 Join our Discord for real-time community support

Stay Connected

from github.com/AaronWander/EmbedMCP

Installing EmbedMCP

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/AaronWander/EmbedMCP

FAQ

Is EmbedMCP MCP free?

Yes, EmbedMCP MCP is free — one-click install via Unyly at no cost.

Does EmbedMCP need an API key?

No, EmbedMCP runs without API keys or environment variables.

Is EmbedMCP hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install EmbedMCP in Claude Desktop, Claude Code or Cursor?

Open EmbedMCP on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare EmbedMCP with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All ai MCPs