mspbots-agent-mcp
MCP server for the MSPbots Agent Platform API — exposes the Agent Platform'sconnector inventory, agent triggers (scheduled/event tasks), and per-agent policy(permissions, self-evaluation, human-in-the-loop approval) to MCP clients.
It follows the same design as the sibling ticketqa-mcp service: stateless, nostored credentials, per-request header authentication over theModel Context Protocol (Streamable HTTP/SSEtransport).
MSPbots Connector
The MCP Gateway built for MSPs: visible, controlled, and your data stays where it belongs.
MSPbots Connector gives your AI clients one MCP URL to reach your entire MSP stack (RMM, PSA, documentation, security, finance), and gives you control over who can call which tool and a record of everything that was called.
Hosted service: connect.mspbots.ai · Free to use.
Why Connector

| Visible | Every tool call is logged: who, which vendor, when. Usage dashboard, CSV export, SIEM streaming. |
| Controlled | Role-based, per-tool allowlist. Deny by default. |
| Protected | Credentials encrypted per record. AI clients never see your vendor passwords or tokens. |
| MSP-native | Built for the MSP stack only: RMM, PSA, documentation, security, finance. |
Quick start
- Sign up at connect.mspbots.ai.
- Connect your vendors. Pick from 150+ connectors and authorize each one on the vendor's own page, or bring your own API keys. No extra vendor accounts needed.
- Set access. Create roles and decide which tools each role may call. Nothing is allowed until you allow it.
- Add your MCP URL to your AI client. Copy the URL from your Connector dashboard into Claude, ChatGPT, Cursor, VS Code, Gemini, Codex, or any MCP-capable client.
- Ask your AI to do real work, and watch every call appear in the audit log.
Features
Unified gateway
- One MCP URL for all your AI clients
- 150+ connectors across the MSP stack
- Custom MCP servers supported, free
- Authorization happens on each vendor's own page; the AI never sees passwords or tokens
- Bring your own credentials: use the vendor API keys you already have
- Multiple accounts per app; connect once, reuse across AI clients
- Tool search: finds the right action for the task
- One sign-in; vendor credentials are injected behind the scenes and OAuth tokens refresh automatically
Visibility
- Audit log for every tool call (who, which vendor, when)
- Usage dashboard: call volume, top vendors, error rate, per-user activity
- Export audit logs to CSV, or stream to Loki, Graylog, or LogScale (SIEM)
- Optional recording of tool arguments and result summaries (off by default, enabled per organization)
Control
- Per-role, per-tool allowlist, deny by default
- owner / admin / member roles
- SCIM provisioning with Entra ID, Okta, JumpCloud, and Google Workspace
Security
- Credentials encrypted one record at a time (AES-256-GCM, per-record key)
- AI clients authenticate with OAuth 2.1 + PKCE
- Vendor responses are scanned for prompt-injection patterns and returned as structured data. This is one layer of defense, not a guarantee against every attack.
- Static egress IP for customer firewall and API allowlisting
Pricing
Free.
Links: connect.mspbots.ai · mspbots.ai
When would you use this
This MCP is meta: it doesn't wrap a customer-facing integration, it wraps theAgent Platform's own admin/config API for an agent. The "users" are platformadmins/builders setting up an agent, or an agent introspecting/adjusting itsown configuration:
- "What connectors does this tenant have hooked up, and which are actuallyconnected right now?" →
mspbotsagent_get_connectors - "What connectors can this agent use / what does it pull data from?" →
mspbotsagent_get_sop_data_sources(agent-scoped — notmspbotsagent_get_connectors, which is tenant-wide) - "Make this agent run every Monday morning to summarize open tickets" →
mspbotsagent_upsert_trigger(typerecurring) - "Kick this agent off automatically whenever a new ConnectWise ticket comesin" →
mspbotsagent_upsert_trigger(typeevent, after checkingmspbotsagent_get_trigger_catalog) - "Lock down what this agent is allowed to do without asking first" →
mspbotsagent_upsert_agent_permissions - "Require a human to sign off before this agent issues a refund" →
mspbotsagent_upsert_agent_approval - "Write up this agent's SOP so it has a documented, repeatable procedure" →
mspbotsagent_set_sop_purpose/ the othermspbotsagent_set_sop_*tools - "What SOPs does this tenant have / which are still drafts?" →
mspbotsagent_list_sops
Tools
Every tool takes its credentials from the request headers(X-API-Key / X-MSP-Tenant-Id / X-MSP-Host) — no token is ever passed as atool argument.
Connectors
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_get_connectors |
List every connector on the current tenant, returning each one's name, whether it is installed, and its connection status | none |
mspbotsagent_list_connector_tools |
List one connector's tools with their per-agent on/off switch state (enabled) |
agent_id, capability_id |
mspbotsagent_set_connector_tools |
Switch a connector's tools on/off for one agent (single or batch) | agent_id, capability_id, tools[], enabled |
mspbotsagent_get_connectors is tenant-scoped: it takes no agent_id andreturns the same inventory regardless of which agent is being configured. Forthe connectors a specific agent is declared to use, seemspbotsagent_get_sop_data_sources instead.
The two switch tools operate on individual tools within a connector. Aconnector's tools all default to on; the backend records only theexceptions (an opt-out list per agent + connector), so a tool with no overridereads enabled: true. capability_id is the connector id returned bymspbotsagent_get_connectors (or by a mspbotsagent_get_sop_data_sourcesentry). mspbotsagent_list_connector_tools returns the same tool names asmspbotsagent_get_sop_data_sources, adding each tool's enabled flag.mspbotsagent_set_connector_tools affects only the tool names you pass;enabled: false switches them off (the agent can no longer see or call them),enabled: true switches them back on. A change takes effect on the agent'snext chat — there is no ~30s restart to wait out and no need to start a newthread. For "all on / all off", read the current names first and pass the wholebatch in one call (restartRequired in the response is always true, butthe agent picks the change up on its next chat regardless; pending: truemeans the switch was saved but not yet synced to the agent's config).
mspbotsagent_get_connectors returns one row per connector:
| Field | Description |
|---|---|
name |
Connector display name |
integration |
Connector/integration key (e.g. connectwise-command) |
scope |
Connector scope (e.g. mspbots) |
managed |
How it is managed (e.g. gateway) |
installed |
Whether it is installed/enabled (bool, from the API's enabled) |
connected |
Whether it is currently connected (bool, from the API's connected) |
status |
Derived status: not_installed / connected / installed_disconnected |
Every connector the API returns also carries a very large base64
logofield —this service strips it to keep responses lean.
Backing endpoint: GET /apps/mb-platform-agent/api/capabilities/connectors, whichreturns {"success": true, "data": {"list": [ {connector}, ... ]}}.
Triggers (agent scheduled / event tasks)
A trigger runs an agent automatically: on a cron schedule (recurring) or when anevent fires in an external integration (event). Its primary key is taskId, and itbelongs to one agentId.
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_list_triggers |
List all of an agent's triggers (paginated) | agent_id (required), page (default 1), page_size (default 50) |
mspbotsagent_upsert_trigger |
Create or modify a trigger (with task_id = modify, without = create) |
agent_id, task_id, name, prompt, type (recurring/event), enabled, expires_in_days; recurring: schedule (cron, 1h minimum), timezone, run; event: trigger_integration, trigger_events |
mspbotsagent_delete_trigger |
Delete a trigger (cannot be undone) | task_id (required) |
mspbotsagent_get_trigger_catalog |
List the integration + events combinations that are valid for event triggers | none |
mspbotsagent_run_trigger 🚫 |
Run one trigger manually, right now (for testing or a catch-up run) — currently not exposed | task_id (required) |
🚫
mspbotsagent_run_triggeris currently not exposed. Its@mcp.tooldecorator is commented out insrc/mspbots_agent_mcp/tools/triggers.py, so itno longer appears intools/listand cannot be called by external clients. Theimplementation stays in that file; re-enable by uncommenting the decorator(and the matching entry intests/test_tools.py). The row above describes itas implemented; the other four trigger tools are unaffected.Creating via
upsertrequiresagent_id/name/prompt/type;recurringalsorequiresschedule(cron, 1h minimum interval), andeventrequirestrigger_integration+trigger_events, whose combination must appear in theget_trigger_catalogcatalog. When modifying, submit only the fields you want tochange.Backing endpoints:
GET|POST /api/tasks,PUT|DELETE /api/tasks/:taskId,GET /api/tasks/trigger-catalog,POST /api/tasks/:taskId/run.
Agent policy (permissions / evaluation / approval)
All three blocks of configuration hang off the same agent record: the reads shareone GET, and the writes share one partial PUT. So do not write to the same agentconcurrently (partial patches overwrite each other); and when a read comes back withpolicyError=true, the upsert tools refuse to write permission/interruptOn back(owners-only updates are unaffected) to avoid persisting a broken policy. The primarykey is agentId throughout.
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_get_agent_permissions |
Read the permission config: permission / interruptOn / owners / tools (read-only) / policyError |
agent_id (required) |
mspbotsagent_upsert_agent_permissions |
Update tool permissions, interrupt settings and ownership (partial; the three keys are independent, so sending owners alone is valid) | agent_id (required), permission (tool→allow/ask/deny), interrupt_on (tool→true or {allowed_decisions, description}), owners ([{userId, name, email}]) — at least one of the three |
mspbotsagent_get_agent_evaluation |
Read the self-evaluation config review = {rules, max_iterations} |
agent_id (required) |
mspbotsagent_upsert_agent_evaluation |
Set/update the self-evaluation rules (an empty rules array turns self-evaluation off) |
agent_id (required), rules (rule array), max_iterations |
mspbotsagent_get_agent_approval |
Read the human approval rules approval (array) |
agent_id (required) |
mspbotsagent_upsert_agent_approval |
Set/update the human approval rules (an empty array removes every approval gate) | agent_id (required), rules (approval rule array) |
Rule object shapes:
- evaluation
rules[]:{ rubric, name, description, triggers: [regex/keywords] } - approval
rules[]:{ name, intent, triggers: [regex/keywords], tools: [...], decisions: [...] }
Backing endpoints:
GET /api/agents/:id(shared by all three getters),PUT /api/agents/:id(shared by all three upserts, partial: writingpermission/interruptOn,review, andapprovalrespectively).
Agent SOP author
An agent's SOP (standard operating procedure) draft has 5 independent fields, each withits own read and write tool, and each field writes through its own separate endpoint(unlike permission/evaluation/approval above, which share a singlePUT /api/agents/:id). Every write has the same shape: PUT {"value": ...}. Theprimary key is agentId.
Even though each field has a different endpoint, all 5 still live on the same agentrecord (the
sopAuthorsub-document), so the rule frompermission/evaluation/approval applies here too: do not write to the same agent'sSOP fields concurrently, or they may overwrite each other. The SOP itself is adocument — a planning draft describing what the agent "should do"; thepermission/evaluation/approval config above is the behavior policy that actually takeseffect at runtime, deciding what the agent "can actually do". The two arecomplementary but distinct kinds of configuration.
🚫 The two
*_sop_proceduretools are currently not exposed. Their@mcp.tooldecorators are commented out insrc/mspbots_agent_mcp/tools/sop_author.py, so they no longer appear intools/listand cannot be called by external clients. The implementations stay inthat file; re-enable by uncommenting the two decorators (and the matching entries intests/test_tools.py). The rows below describe them as implemented; the other fourSOP fields are unaffected.
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_get_sop_name |
Read the SOP name | agent_id (required) |
mspbotsagent_set_sop_name |
Set the SOP name (non-empty, ≤60 chars, unique within the tenant, cannot be cleared) | agent_id (required), value (required) |
mspbotsagent_get_sop_source |
Read the source (the original task description the SOP was written from) | agent_id (required) |
mspbotsagent_set_sop_source |
Set the source (pass null to clear it) | agent_id (required), value (required, string or null) |
mspbotsagent_get_sop_purpose |
Read the purpose (markdown) | agent_id (required) |
mspbotsagent_set_sop_purpose |
Set the purpose (markdown) | agent_id (required), value (required) |
mspbotsagent_get_sop_data_sources |
Read the dataSources list (structured object) | agent_id (required) |
mspbotsagent_set_sop_data_sources |
Set the dataSources list (structured object) | agent_id (required), value (required, object) |
mspbotsagent_get_sop_procedure 🚫 |
Read the procedure (markdown) — currently not exposed | agent_id (required) |
mspbotsagent_set_sop_procedure 🚫 |
Set the procedure (markdown) — currently not exposed | agent_id (required), value (required) |
The dataSources value shape (each source stores only integration, nopreconditions):
{
"sources": [
{ "integration": "open-meteo" },
{ "integration": "ms-graph" }
]
}
Every entry mspbotsagent_get_sop_data_sources reads back is a connector (an MCPserver available to the agent); besides integration it carries connection status anda tool list:
| Field | Type | Meaning |
|---|---|---|
integration |
string | Data source key |
org |
bool, optional | Present only when the source was added at the organization level |
found |
bool | false = the referenced connector has been deleted or disabled and is unusable; treat it as non-existent |
name / description / transport / endpoint |
string | Connector display info and its MCP endpoint (present only when found) |
connection |
string | Current connection status, see the table below. Agent level wins over org level |
tenantConnected |
bool | Whether an organization/tenant-level connection exists |
enabled |
bool | Whether the connector is enabled at the organization level |
managed |
string, optional | "gateway" for gateway-managed connectors |
tools |
array | The tools this connector provides ({name, label, description}); empty when not connected or when discovery failed |
connection values:
| Value | Meaning | How to treat it |
|---|---|---|
"agent" |
This agent connected with its own account (agent-level connection) | Usable |
"org" |
It is using the organization/tenant-level connection | Usable |
"none" |
Not connected (or the connector is disabled for the organization) | Not usable; the user has to connect it first |
"unavailable" |
The gateway status could not be read | ⚠️ Does not mean disconnected — it just could not be read at this moment. Do not call it "not connected" on this basis |
Two easy mistakes, both written into the tool descriptions:
connectionis a single value and agent wins. When both levels are connected,connectionshows only"agent", and the tenant half is only visible intenantConnected. There is currently no symmetric standaloneagentConnectedboolean, so agent level can only be detected viaconnection === "agent"."none"and"unavailable"must be handled differently: the former really is notconnected (fine to prompt the user to go connect it), while the latter merely meansthe status could not be read (lean towards still-usable, and do not misreport it).
Summary test: usable = found === true and connection ∈ {agent, org} (withunavailable leaning usable); unusable = found === false or connection === "none".
mspbotsagent_get_sop_data_sourcesis the agent-scoped answer to"what connectors does this agent use", and its rows carry connectionstatus, so it usually answers "can this agent use X" on its own.mspbotsagent_get_connectorsis the tenant-wide inventory— reach for it when the question is not about a particular agent.
Backing endpoints:
GET|PUT /api/agents/:id/sop-author/{name,source,purpose,data-sources-list,procedure}.
Agent skills
🚫 Currently not exposed. These tools are commented out in
create_mcp_server()(src/mspbots_agent_mcp/server.py), so they do not appear intools/listand cannot be called by external clients. The implementation stays insrc/mspbots_agent_mcp/tools/skills.py; re-enable by uncommenting theskillsimport andskills.register(...)call there (and the matching entries intests/test_tools.py). The rest of this section describes them as implemented.
The skills available to an agent come in three kinds: mspbots (built into theplatform), org (shared across the organization), and agent (private to that agent).Only private (agent) skills can be created/edited/deleted here; mspbots/orgskills can only be read, not changed. The primary key is capabilityId (the id oneach record from the list endpoint).
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_list_agent_skills |
List every skill available to an agent (org-shared + that agent's private ones + platform mspbots ones) and whether each is switched on |
agent_id (required) |
mspbotsagent_create_agent_skill |
Create a new private skill (scope=agent) for an agent, publish it as an npm version and install it into that agent's workspace | agent_id (required), name (required), files (required, must contain a SKILL.md) |
mspbotsagent_update_agent_skill_files |
Edit an agent's private skill files and publish a new version (whole-package replacement). Private skills owned by that agent only | agent_id (required), capability_id (required), files (required, the complete file set), note (optional, defaults to "Edit") |
mspbotsagent_delete_agent_skill |
Delete an agent's private skill: soft-delete the capability + clear the opt-out row + uninstall it from the workspace | agent_id (required), capability_id (required) |
mspbotsagent_set_agent_skill_enabled |
Turn one skill on or off for an agent without touching its files. Works on all three kinds, not just private ones | agent_id (required), ref (required, from the list tool), enabled (optional, default true), skill_type (optional, mspbots/org/agent, default org) |
mspbotsagent_list_agent_skills returns, for each skill,id/type (mspbots|org|agent)/ref/scope/name/skillName/description/version/enabled/selected(true when not opted out)/available, plus a selectedIds list. selected isexactly the switch mspbotsagent_set_agent_skill_enabled writes, and each entry'sref and type are what to pass back in as ref and skill_type.
⚠️ The field structure of each element of
files("SkillFile") is not specified:the API spec handed over only said"$ref": "#/definitions/SkillFile", withoutattaching that definitions block.This implementation assumes{"path": "<relative path, e.g. 'SKILL.md'>", "content": "<raw text content>"}— inferred from the common SKILL.md packaging convention, not verified against thereal API. Confirm the actual field names with the backend before relying on it(whether there is also anencoding/executable, etc.).Backing endpoints:
GET /api/agents/:id/skills,POST /api/agents/:id/skills/create,PUT /api/agents/:id/skills/:capabilityId/files,PUT /api/agents/:id/skills,DELETE /api/agents/:id/skills(the last one's request body is{"id": "<capabilityId>"}— a DELETE with a body isunusual in HTTP, so this service sends it through httpx's low-levelrequest()rather thandelete()).
PUT /api/agents/:id/skillstakes{ref, enabled, type}and answers{agentId, ref, enabled, restartRequired, pending}.restartRequiredis alwaystrue.pending: truemeans the flag was recorded but syncing the install/uninstallto the runtime failed, so the agent's capability is marked pending and will be retriedlater — the change is not in effect yet. An unresolvablerefor an unknown agentcomes back as HTTP 200 with{"success": false, "error": "…"}rather than a 4xx, sothe tool checkssuccessexplicitly and reports it as an error.
SOP library (list + synchronous chat)
🚫
mspbotsagent_chat_with_sopis currently not exposed. Its@mcp.tooldecorator is commented out insrc/mspbots_agent_mcp/tools/sops.py, so it nolonger appears intools/listand cannot be called by external clients (itsdedicated tests intests/test_sops.pyare@pytest.mark.skip-ed to match).The implementation stays in that file; re-enable by uncommenting thedecorator (and the matching entry intests/test_tools.py, and the skippedtests). Everything below describes it as implemented;mspbotsagent_list_sopsis unaffected and still exposed.
A SOP (standard operating procedure) is a tenant-level record, and creating onealso opens a dedicated agent for it. That agent is whatmspbotsagent_chat_with_sop talks to, which is why it takes only sop_id, neveragent_id. This is a different thing from Agent SOP author above:those tools edit the SOP draft sections attached to an agent, while these read theSOP library and actually run a turn of conversation.
| Tool | What it does | Parameters |
|---|---|---|
mspbotsagent_list_sops |
List the tenant's SOPs with pagination, returning each one's status and owning agent | search (fuzzy match on name only, does not search the body), status (draft/published), page (default 1), page_size (default 20, max 100) |
mspbotsagent_chat_with_sop 🚫 |
Hold one synchronous turn of conversation with a SOP's agent: blocks until the whole turn finishes, then returns the reply — currently not exposed | sop_id (required), message (required), thread_id (continue the same conversation), new_thread (start a new one) |
mspbotsagent_list_sops returns the backend's GET /api/sops response as-is({total, list}; every field on each row, snake_case, nothing projected away —e.g. id/name/description/status/source/tags/agent_id/agent_live/updated_at).agent_live being false means that agent record no longer exists — the SOP is stillreadable, but it cannot be talked to, and no parameter on either tool can bring it back.
How "the user names a SOP in their own words" turns into a sop_id:the backend's search only does name ILIKE '%…%' (service/sop/db.ts:88) — it doesnot match description / tags / body, and there is no fuzzy, synonym or cross-languagematching. So a miss does not mean it does not exist; it usually just means thewording differs (the user says "the refund one", the SOP is called Refund Handling).Dropping the user's own words straight into search, getting 0 rows, and thenanswering "there is no such SOP" is the most expensive mistake on this path. The rightmove is to not pass search, read each row's name + description and pick ityourself, using total to decide whether to page. Use search only for a name fragmentyou already know for certain. This guidance lives in the search parameter's owndescription (parameter descriptions do not count towards the 500-char limit), and isguarded by test_search_param_warns_that_a_miss_is_not_an_absence intests/test_sops.py.
When you only have an agent_id: every list_sops row carries agent_id, so listthem and match on it to get the sop_id; mspbotsagent_get_usage_overview rows carryboth sopId and agentId too. Do not try to parse the agent's name — although a SOPagent is created as ${sop.name} #${sop.id} and renames propagate, PUT /api/agents/:idlets a user change name directly, which decouples it immediately, and an ordinaryagent can be given the same name too.
mspbotsagent_chat_with_sop returnssopId/sopName/agentId/threadId/status/reply, where status explains anempty reply:
status |
Meaning |
|---|---|
completed |
Ran normally; reply is the last user-visible assistant message of this turn |
interrupted |
Paused on a human approval gate, with pausedActions attached; someone has to decide in the Agent Platform before it continues |
no_reply |
Finished, but produced no assistant message |
reply is extracted the same way the frontend's lastAssistantText does it incomponents/assistant-ui/aegra/messages.ts: scan backwards for atype=ai/role=assistant message, skipping the internal messages the UI hides(rubric_grader, sop_context, sop_suffix, compact_summary, task_notification).
⚠️ This call path differs from the other tools in two ways — do not revert them tothe defaults:
- It turns retries off (
retries=0). A request that runs an agent is notidempotent — re-sending after a lost response runs the agent again instead ofrecovering the first result.- It raises the read timeout to 300s (the 30s default cannot survive a real agentturn; Aegra's own backstop is 1 hour). The error returned on timeout carries
retryable=falseand says explicitly: do not re-send, ask again on the sameconversation instead.Backing endpoints:
GET /api/sops(returns{list, total}when paginated),GET /api/sops/:id(to getagent_id),POST /api/agents/:id/run/wait. That lastone signals a failed run with HTTP 200 +{"success": false}rather than a4xx/5xx, so this tool checks thesuccessfield explicitly to avoid reading a failureas "succeeded but said nothing".
Quick Start
Docker (recommended)
docker compose up --build
The server starts on http://localhost:8080.
Local (uv)
uv sync
python -m mspbots_agent_mcp
Health Check
curl http://localhost:8080/health
# {"status": "ok"}
No credentials are required for the health endpoint.
Authentication
Every request to /mcp must include the following HTTP headers (provided by theMCP caller — kept consistent with ticketqa-mcp):
| Header | Type | Required | Description | Example |
|---|---|---|---|---|
X-API-Key |
string | Yes | An access credential already issued by the Agent Platform (JWT bearer token). This service forwards it verbatim as the downstream request's Authorization: Bearer <token>. |
X-API-Key: <jwt-bearer-token> |
X-MSP-Tenant-Id |
string | Yes | Tenant identifier. Renamed to the X_Tenant_ID header when forwarded to the downstream API (the tenant is also embedded in the JWT). |
X-MSP-Tenant-Id: <tenant-id> |
X-MSP-Host |
string | Yes | The host the Agent API lives on. | X-MSP-Host: https://agent.mspbots.ai |
⏳ Transition (from 2026-09-23):
X-API-Keyreplaces the formerX-MSP-Token. Credentials saved before the rename still sit in the registryunder the old name and the gateway injects them verbatim, so the middlewarefalls back to readingX-MSP-TokenwhenX-API-Keyis absent. When both arepresentX-API-Keywins. Once every tenant credential has been re-saved underthe new name, delete the fallback inserver.pyandtests/test_middleware.py::test_legacy_token_header_is_still_accepted.
X-MSP-Tenant-Idis not affected by this rename — still required, stillforwarded downstream.
Missing any of the three headers returns 401 Unauthorized.
Environment Variables
| Variable | Default | Description |
|---|---|---|
MCP_HTTP_PORT |
8080 |
Listening port |
MCP_HTTP_HOST |
0.0.0.0 |
Listening host |
MCP Endpoint
POST http://localhost:8080/mcp
Connect your MCP client with:
- Transport:
http(Streamable HTTP / SSE) - Headers:
X-API-Key,X-MSP-Tenant-Id,X-MSP-Host(all required)
Test Example
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-API-Key: <token>" \
-H "X-MSP-Tenant-Id: <tenant-id>" \
-H "X-MSP-Host: https://agent.mspbots.ai" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "mspbotsagent_get_connectors", "arguments": {} }
}'
⚠️ This is a public repository. Never write real tokens, tenant ids or othersensitive values into any committed file — the
<token>/<tenant-id>above areplaceholders only.