AgentStack
MCP verified MIT Self-run

EmbedMCP

mcp-aaronwander-embedmcp · by AaronWander

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

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add mcp-aaronwander-embedmcp

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of EmbedMCP? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

EmbedMCP - Embedded MCP Server Library

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.

[](https://opensource.org/licenses/MIT) [](https://en.wikipedia.org/wiki/C99) [](#platform-support) [](https://modelcontextprotocol.io/)

[English](./README.md) • [简体中文](./README_zh.md)

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

``bash git clone https://github.com/AaronWander/EmbedMCP.git cd EmbedMCP ``

  1. Copy to your project

``bash 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 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](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.

Stay Connected

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.