Skip to content

Commit ef3fd31

Browse files
author
Nicola Spieser
committed
feat: removePrompt/removeRoot/removeResourceTemplate, ASSERT_STR_NOT_CONTAINS — bump to v0.27.4
- Add removePrompt(), removeRoot(), removeResourceTemplate() methods - Add ASSERT_STR_NOT_CONTAINS test macro - Add 7 tests for new removal methods (827 total) - Bump version to 0.27.4
1 parent 162eb1c commit ef3fd31

8 files changed

Lines changed: 113 additions & 8 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
|---|:---:|:---:|:---:|
3434
| Runs on the MCU ||| ❌ CLI tool |
3535
| MCP spec compliant | ✅ 2025-03-26 | ❌ custom WS ||
36-
| Actually compiles |820 tests | ❌ self-described | N/A |
36+
| Actually compiles |827 tests | ❌ self-described | N/A |
3737
| Streamable HTTP + SSE ||||
3838
| WebSocket transport ||||
3939
| Claude Desktop bridge ||||

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mcpd",
3-
"version": "0.27.3",
3+
"version": "0.27.4",
44
"description": "MCP Server SDK for Microcontrollers — Expose ESP32/RP2040/STM32 hardware as AI-accessible tools via Model Context Protocol",
55
"keywords": [
66
"mcp",

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=mcpd
2-
version=0.27.3
2+
version=0.27.4
33
author=Nicola Spieser
44
maintainer=Nicola Spieser <redbasecap-buiss@users.noreply.github.com>
55
sentence=MCP Server SDK for Microcontrollers

src/mcpd.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,36 @@ bool Server::removeResource(const char* uri) {
102102
return false;
103103
}
104104

105+
bool Server::removeResourceTemplate(const char* uriTemplate) {
106+
for (auto it = _resourceTemplates.begin(); it != _resourceTemplates.end(); ++it) {
107+
if (it->uriTemplate == uriTemplate) {
108+
_resourceTemplates.erase(it);
109+
return true;
110+
}
111+
}
112+
return false;
113+
}
114+
115+
bool Server::removePrompt(const char* name) {
116+
for (auto it = _prompts.begin(); it != _prompts.end(); ++it) {
117+
if (it->name == name) {
118+
_prompts.erase(it);
119+
return true;
120+
}
121+
}
122+
return false;
123+
}
124+
125+
bool Server::removeRoot(const char* uri) {
126+
for (auto it = _roots.begin(); it != _roots.end(); ++it) {
127+
if (it->uri == uri) {
128+
_roots.erase(it);
129+
return true;
130+
}
131+
}
132+
return false;
133+
}
134+
105135
void Server::notifyToolsChanged() {
106136
JsonDocument doc;
107137
doc["jsonrpc"] = "2.0";

src/mcpd.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
#include "MCPTransportBLE.h"
4343
#endif
4444

45-
#define MCPD_VERSION "0.27.3"
45+
#define MCPD_VERSION "0.27.4"
4646
#define MCPD_MCP_PROTOCOL_VERSION "2025-03-26"
4747

4848
namespace mcpd {
@@ -302,6 +302,21 @@ class Server {
302302
*/
303303
bool removeResource(const char* uri);
304304

305+
/**
306+
* Remove a resource template by URI template. Returns true if found and removed.
307+
*/
308+
bool removeResourceTemplate(const char* uriTemplate);
309+
310+
/**
311+
* Remove a prompt by name. Returns true if found and removed.
312+
*/
313+
bool removePrompt(const char* name);
314+
315+
/**
316+
* Remove a root by URI. Returns true if found and removed.
317+
*/
318+
bool removeRoot(const char* uri);
319+
305320
#ifdef MCPD_TEST
306321
public: // Allow test access to internals
307322
#else

test/test_framework.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ static int _tests_failed = 0;
5050
throw "String does not contain: " #needle; \
5151
} while(0)
5252

53+
#define ASSERT_STR_NOT_CONTAINS(haystack, needle) \
54+
do { \
55+
std::string _h(haystack); \
56+
if (_h.find(needle) != std::string::npos) \
57+
throw "String unexpectedly contains: " #needle; \
58+
} while(0)
59+
5360
#define TEST_SUMMARY() \
5461
do { \
5562
printf("\n ────────────────────────────────────────\n"); \

test/test_infrastructure.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ TEST(completion_manager_no_providers) {
719719
// ═══════════════════════════════════════════════════════════════════════
720720

721721
TEST(version_constants) {
722-
ASSERT_EQ(String(MCPD_VERSION), String("0.27.3"));
722+
ASSERT_EQ(String(MCPD_VERSION), String("0.27.4"));
723723
ASSERT_EQ(String(MCPD_MCP_PROTOCOL_VERSION), String("2025-03-26"));
724724
}
725725

test/test_jsonrpc.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ TEST(version_is_0_11_0_compat) {
712712
auto* s = makeTestServer();
713713
String req = R"({"jsonrpc":"2.0","id":250,"method":"initialize","params":{}})";
714714
String resp = s->_processJsonRpc(req);
715-
ASSERT_STR_CONTAINS(resp.c_str(), "\"version\":\"0.27.3\"");
715+
ASSERT_STR_CONTAINS(resp.c_str(), "\"version\":\"0.27.4\"");
716716
}
717717

718718
// ── v0.6.0 Tests: Tool Annotations ────────────────────────────────────
@@ -1567,7 +1567,7 @@ TEST(version_0_11_0) {
15671567
Server* s = makeTestServer();
15681568
String req = R"({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test"}}})";
15691569
String resp = s->_processJsonRpc(req);
1570-
ASSERT_STR_CONTAINS(resp.c_str(), "0.27.3");
1570+
ASSERT_STR_CONTAINS(resp.c_str(), "0.27.4");
15711571
}
15721572

15731573
// ── Watchdog Tool Tests ────────────────────────────────────────────────
@@ -1811,7 +1811,7 @@ TEST(diagnostics_version_macros) {
18111811
ASSERT(strlen(MCPD_VERSION) > 0);
18121812
ASSERT(strlen(MCPD_MCP_PROTOCOL_VERSION) > 0);
18131813
ASSERT_STR_CONTAINS(MCPD_MCP_PROTOCOL_VERSION, "2025");
1814-
ASSERT_STR_CONTAINS(MCPD_VERSION, "0.27.3");
1814+
ASSERT_STR_CONTAINS(MCPD_VERSION, "0.27.4");
18151815
}
18161816

18171817
// ── Batch JSON-RPC edge cases ──────────────────────────────────────────
@@ -2824,6 +2824,59 @@ TEST(relay_interlock_concept) {
28242824
ASSERT_STR_CONTAINS(resp.c_str(), "heater turned OFF");
28252825
}
28262826

2827+
// ── Remove Prompt/Root/ResourceTemplate Tests ──────────────────────────
2828+
2829+
TEST(remove_prompt_by_name) {
2830+
auto* s = makeTestServer();
2831+
// Server already has "greet" prompt from makeTestServer
2832+
ASSERT(s->removePrompt("greet") == true);
2833+
String req = R"({"jsonrpc":"2.0","id":900,"method":"prompts/list","params":{}})";
2834+
String resp = s->_processJsonRpc(req);
2835+
ASSERT_STR_CONTAINS(resp.c_str(), "\"prompts\":[]");
2836+
}
2837+
2838+
TEST(remove_prompt_nonexistent) {
2839+
auto* s = makeTestServer();
2840+
ASSERT(s->removePrompt("no_such_prompt") == false);
2841+
}
2842+
2843+
TEST(remove_prompt_idempotent) {
2844+
auto* s = makeTestServer();
2845+
ASSERT(s->removePrompt("greet") == true);
2846+
ASSERT(s->removePrompt("greet") == false);
2847+
}
2848+
2849+
TEST(remove_root_by_uri) {
2850+
auto* s = makeTestServer();
2851+
s->addRoot("file:///workspace", "Workspace");
2852+
s->addRoot("file:///config", "Config");
2853+
ASSERT(s->removeRoot("file:///workspace") == true);
2854+
String req = R"({"jsonrpc":"2.0","id":910,"method":"roots/list","params":{}})";
2855+
String resp = s->_processJsonRpc(req);
2856+
ASSERT_STR_NOT_CONTAINS(resp.c_str(), "workspace");
2857+
ASSERT_STR_CONTAINS(resp.c_str(), "config");
2858+
}
2859+
2860+
TEST(remove_root_nonexistent) {
2861+
auto* s = makeTestServer();
2862+
ASSERT(s->removeRoot("file:///nope") == false);
2863+
}
2864+
2865+
TEST(remove_resource_template_by_uri) {
2866+
auto* s = makeTestServer();
2867+
s->addResourceTemplate("sensor://{id}/reading", "Sensor", "Read sensor", "application/json",
2868+
[](const std::map<String, String>&) -> String { return "{}"; });
2869+
ASSERT(s->removeResourceTemplate("sensor://{id}/reading") == true);
2870+
String req = R"({"jsonrpc":"2.0","id":920,"method":"resources/templates/list","params":{}})";
2871+
String resp = s->_processJsonRpc(req);
2872+
ASSERT_STR_CONTAINS(resp.c_str(), "\"resourceTemplates\":[]");
2873+
}
2874+
2875+
TEST(remove_resource_template_nonexistent) {
2876+
auto* s = makeTestServer();
2877+
ASSERT(s->removeResourceTemplate("nope://{x}") == false);
2878+
}
2879+
28272880
// ── Main ───────────────────────────────────────────────────────────────
28282881

28292882
int main() {

0 commit comments

Comments
 (0)