Canopy
The Canopy server provides vehicle diagnostic and CAN-bus data analysis capabilities through four tools:
list_available_signals: Discover all signals exposed by the connected data source (e.g., OBD, CAN+DBC, synthetic), including units and typical ranges. Always call this first to know what signals are available before analysis.summarize_session: Get a structural overview of a data session — which signals are present, sample counts, coverage gaps, and finding counts by severity — without interpretation, so you understand what data exists before diving deeper.get_signal: Retrieve time-series data for a specific signal over a defined time range, returned with explicit units and timestamps. Supports downsampling, handles point reads, and returns structured errors for unknown signals instead of guessing.run_diagnostic_rules: Execute predefined diagnostic rules over a time range, producing structured findings with cited evidence (actual data samples) and confidence levels. Rules requiring unavailable signals are skipped and reported, distinguishing "nothing is wrong" from "we couldn't look."
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Canopywhat signals are available for engine diagnostics?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Canopy
An MCP server that exposes vehicle-diagnostic and CAN-bus domain logic as agent tools, with a LangGraph orchestration layer and a human-in-the-loop eval harness.
▶ Live showcase — replay real recorded traces: a cited answer and a grounded refusal, with the honest eval numbers. No install, no API key.
Status: Phase 4 (evals & human-in-the-loop) complete. A LangGraph agent answers
natural-language diagnostics questions with validated, cited structured output — or refuses,
grounded, when the connected source can't answer. Every consequential output can pass through
a human-review interrupt whose structured corrections become permanent regression cases, and
an LLM-judge scores traces against the same taxonomy the human uses. The design lives in
docs/; the build ships one phase at a time.
Evaluation — the headline number
The LLM-judge agrees with human review on 85% of traces (n=20). Inter-rater reliability could not be measured with a panel — this is a solo project — so the same subset was scored twice, one week apart, reaching 90% self-agreement. The judge should therefore be read as approaching, not exceeding, the reliability ceiling of its ground truth. Agreement is perfect on the mechanically checkable failures —
hallucinated_valueandabsence_as_negation, which a judge can verify against the trace — and every disagreement was anoverconfidentcall (85%), a judgment a rubric only partially disciplines.
That paragraph is the point of Phase 4: a number, measured honestly, with its ceiling and its
weak spot stated rather than hidden. That figure is the illustration — hand-seeded labels (a
solo project has no panel), reproducible with no API key: uv run python scripts/calibrate.py.
The machinery is real; the number is a rehearsal for the one below.
First real pass — the machinery on collected labels (n=8)
The first calibration on real labels — one human review pass over 8 captured traces — reads differently, and the gap is the finding:
50% judge–human agreement (n=8). Agreement is 100% on every failure mode except
false_refusal(50%), which alone drags the headline down: the judge waved through four refusals that a human marked as answerable questions wrongly declined. No self-agreement ceiling yet — that needs a second review pass a week apart. Reproduce from recorded labels withuv run python scripts/calibrate.py --real; the report iscalibration_report_realpass_a.json.
This is a deliberately pre-fix snapshot: the same over-refusal shows up independently in the
regression suite (scripts/eval.py → 3/6, all three failures answerable questions refused). The
next moves are an agent fix — each false refusal minted as a from_review regression case and
tracked run-over-run (evals/tracking.py) — then review pass B
for the ceiling. Publishing the low number before the fix is the honest version of the story.
Why a structured taxonomy, not thumbs-up/down? A thumbs-down says the answer was bad. A
structured ErrorType says which of my defenses failed —
whether a tool description needs a sentence, a refusal path didn't trigger, or a validator has
a gap. The taxonomy is derived from the architecture's known weak points, so every label points
at a fix.
Related MCP server: Deep Agent Harness Automation System
Architecture
The central bet: the GenAI layers are independent of the data source. A normalizer sits between the data and the intelligence, so swapping OBD for raw CAN later does not require rewriting the tools, the agent, or the evals.
┌──────────────────────────────────────────────────┐
│ L6 Evals & human-in-the-loop │ GenAI ✅ Phase 4
│ L5 Structured outputs & validation │ GenAI ✅ Phase 3
│ L4 Agent orchestration (LangGraph) │ GenAI ✅←core Phase 3
│ L3 MCP server │ GenAI ✅ Phase 2
│ L2 Tool design & schemas │ GenAI ✅ Phase 1
╞══════════════════════════════════════════════════╡ ← THE SEAM
│ L1b Domain logic (diagnostic rules) │ expertise ✅ Phase 0
│ L1a Normalizer (SignalSample / SignalSeries) │ contract ✅ Phase 0
│ L0 Data access: synthetic | OBD | CAN+DBC │ plumbing ✅ synthetic
└──────────────────────────────────────────────────┘Everything above the seam must be ignorant of whether a number came from an OBD PID or a decoded CAN frame. A seam-enforcement test fails CI if that leaks.
What's built (Phases 0–4)
The normalizer contract (
model/signals.py) —SignalSample,SignalSeries,SignalSource. A time-ranged read is the general case; an OBD "value now" read is just a series of length one (is_point_read). Units always travel with values.Structured findings (
model/findings.py) —Findingwith mandatoryevidence: a rule that asserts without citing samples is one the agent would launder into a hallucination.The data-access protocol (
readers/base.py) —SignalReaderwith a first-classavailable_signals(), the mechanism by which the agent will later know what it cannot answer.A deterministic synthetic reader (
readers/synthetic.py) — seeded waveforms for a canonical OBD signal set, with injectable known anomalies. The fixture Layers 2–6 are developed and eval'd against; no hardware required.The first diagnostic rule (
domain/rules/correlation.py) — coolant rising while engine load is only moderate. Assumes a timeseries; degrades to a low-confidence finding on a point read.Four schema'd tools (
tools/) —list_available_signals,summarize_session,get_signal,run_diagnostic_rules. Each is a Pydantic input schema + a description written as a prompt fragment + a handler that returns structured errors (withavailable_signalsand a hint) instead of raising into the agent loop.The MCP server (
mcp/server.py) — a thin stdio adapter over the tool layer. Schemas go over the wire asmodel_json_schema()verbatim; tool errors returnisErrorpayloads the model can recover from, while protocol errors stay JSON-RPC errors the model never sees. Reader selection is env-driven (CANOPY_SOURCE, resolved below the seam byreaders/factory.py) — the server never learns which source it is serving.The LangGraph agent (
agent/graph.py) —agent → tools → validate → refuseas a state graph. Structured output arrives through asubmit_answertool schema; validation failure is a turn (the Pydantic error is fed back with the failing field and a legal escape), capped at two retries, then degrading to a code-built honest answer rather than crashing. The iteration cap converts to a forced-answer degraded turn.The grounded refusal path (
agent/contracts.py) — the headline behavior: a question the source cannot answer produces aRefusalnaming the missing signal and what is available, filled by code from a tool result, never from model self-knowledge. A cross-validator rejects any answer citing a signal the trace never retrieved — confabulation caught mechanically.The eval harness & HITL (
evals/) — a reviewableTrace(full tool-call record, skipped rules, outcome); a review gate built as a LangGraph interrupt whosecorrectverdicts mintfrom_reviewregression cases; a structured feedback taxonomy derived from the architecture's weak points; a regression runner with deterministic fixtures and hard assertions that run in CI; and a calibrated LLM-judge scoring the trace, not just the answer.
Trade-offs (honest)
Synthetic-first, not hardware-first. If the architecture required a dongle to test, it would be wrong. Synthetic data is deterministic, so it doubles as the eval fixture. The cost: synthetic waveforms are plausible, not real — real captures arrive in Phase 5.
A normalizer up front costs an afternoon. The payoff is that adding raw CAN later touches nothing above the seam. If it turns out to leak, that's recorded honestly in the build log — a diagnosed leak is a better story than a lucky clean run.
OBD's coverage ceiling is a feature. OBD cannot see ADAS/camera signals. The right behavior when asked is a grounded refusal, not a guess — that refusal path is a core deliverable, not an edge case.
IP hygiene
No proprietary CAN databases, captures, or signal definitions — ever. Only public OBD-II
PIDs, open DBCs, and synthetic/self-captured logs. See data/README.md
for the provenance ledger.
Develop
uv sync --extra dev # create env + install deps
uv run pytest # run the suite, including the seam test
uv run ruff check . # lintPhase 0 done-signal:
from datetime import datetime, timedelta
from canopy.readers.synthetic import SyntheticReader
series = SyntheticReader(seed=42).read(
"EngineRPM", datetime(2026, 1, 1), datetime(2026, 1, 1) + timedelta(seconds=10)
)
print(len(series.samples), series.unit, series.sample_rate_hz, series.is_point_read)Phase 2 done-signal — a bare MCP client drives the real server as a subprocess (discovery, invocation, structured errors, clean shutdown; no LLM anywhere):
uv run python scripts/smoke_mcp.pyTo explore the same server interactively, register it with any MCP client — e.g. in
Claude Desktop's claude_desktop_config.json:
{
"mcpServers": {
"canopy": {
"command": "/absolute/path/to/repo/.venv/bin/python",
"args": ["-m", "canopy.mcp"],
"env": { "CANOPY_SOURCE": "synthetic" }
}
}
}The same server, zero code changes, backs the Phase 3 LangGraph agent — that is the decoupling MCP buys.
Phase 3 done-signal — ask the agent a question (needs a provider key in .env; copy
.env.example). A grounded refusal on an unanswerable question is a success:
uv run python scripts/ask.py "Is the engine overheating?"
uv run python scripts/ask.py "Did the rear camera activate within 2 seconds?" # → refusalWeb UI — chat with the agent
A two-pane workspace: a chatbot on the left, and on the right the full tool-call trace, the cited answer (or grounded refusal), and an evidence chart annotated with the exact samples the agent cited. Each question runs the real agent; the answer shape is identical to the recorded traces, so the same chart pipeline draws both.
uv run python scripts/serve.py # → http://127.0.0.1:8000Live runs need a provider key in
.env(copy.env.example); the default provider is Gemini's free tier. Override with--provider anthropicor theCANOPY_PROVIDER/CANOPY_MODELenv vars.No key? It still works.
POST /api/askdegrades to replaying the closest recorded trace, so the whole UI is usable offline — the badge just reads replay instead of live.The Simulated scenario selector (Normal / Overheat) chooses the ground-truth condition a live run reads, so you can reproduce the overheat chart on demand. It maps to
build_reader(scenario=…)below the seam — the UI never names a data source.
The page is served straight from site/; its data.js is regenerated from the
recorded traces with uv run python site/build_data.py.
Phase 4 done-signal — the eval harness. Hard assertions run hermetically in the test suite (no key); the live replay and the judge run against a real model; the calibration number is reproducible from recorded labels with no key:
uv run pytest tests/test_eval_runner.py # regression suite, scripted model, no key
uv run python scripts/calibrate.py # the 85% / 90% agreement report
uv run python scripts/eval.py --judge # live replay + LLM-judge (needs a key)Available Tools
4 toolsget_signalA
Retrieves one signal over a time range, returned as a timeseries with explicit units and timestamps.
The name must exactly match a name from list_available_signals. Calling this with an unknown name returns a structured error, not an estimate.
Different data sources have very different sample rates. A request-response source may return a SINGLE sample (a 'point read'), with actual_sample_rate_hz set to null. Do NOT perform timing analysis on a point read — check actual_sample_rate_hz before reasoning about how a signal changed over time.
Results are downsampled to max_samples. If truncated is true, the series is a decimation of the full data and fine timing detail may be lost.
Keep the time range bounded to the interval you actually need. An excessively wide window returns a window_too_large error, not data — and because results are downsampled to max_samples anyway, a wider window buys no extra resolution.
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | End of the time range, inclusive, ISO 8601. | |
| name | Yes | Canonical signal name, exactly as returned by list_available_signals. Case-sensitive. Do not guess or abbreviate. | |
| start | Yes | Start of the time range, inclusive, ISO 8601. | |
| max_samples | No | Downsampling cap. The full series is decimated to at most this many evenly-spaced samples. Raise it only when fine timing detail matters; large values consume context without adding insight. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Given no annotations, description fully discloses behavior: error conditions (unknown name, window_too_large), return format with units/timestamps, sample rate variability, decimation behavior (downsampled to max_samples, truncated flag), and warning about point reads. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Approximately 12 sentences in short paragraphs, front-loaded with purpose. No unnecessary repetition; every sentence adds value (error handling, sampling details, usage constraints). Efficient despite comprehensive information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema and 4 parameters, description covers return format, errors, sampling behavior, and constraints. No gaps remain for agent to infer dangerously; complete enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (all parameters have descriptions), but description adds significant context: name must be exact and case-sensitive from list_available_signals, max_samples explanation (downsampling cap, trade-offs), start/end are inclusive ISO 8601. Adds rationale and usage tips beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states verb 'retrieves', resource 'one signal over a time range', and return format 'timeseries with explicit units and timestamps'. Distinguishes from sibling tools (list_available_signals, run_diagnostic_rules, summarize_session) by focusing on signal data retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidelines: name must exactly match list_available_signals, unknown name returns error, sample rate differences, point reads (actual_sample_rate_hz null) should not be used for timing, downsampling behavior, time range bounding to avoid errors. Clearly tells when and how to use the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_available_signalsA
Returns the complete list of signals available from the currently connected data source, with units and typical ranges.
Call this FIRST whenever you are unsure whether a signal exists. Signal availability depends entirely on the data source, so the only reliable way to know what you can answer is to ask.
If the signal a user asks about does not appear in this list, it is NOT available: do not attempt to retrieve it, do not estimate it, and do not substitute a related signal. Tell the user the signal is unavailable and say which source is connected.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It transparently states that signal availability depends on the data source, and that absence from the list means unavailability. It also mentions the return includes units and typical ranges. It doesn't mention side effects or auth, but for a read-only list operation, this is sufficient and clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences: first states purpose, second gives usage guidance, third sets a rule. It is front-loaded with purpose and contains no extraneous information. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description adequately covers return format (list with units and ranges) and provides behavioral context for missing signals. The tool has no parameters, and the description is complete for its simple scope.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has no parameters and schema coverage is 100%. The description adds value by confirming 'No parameters. Returns everything the current data source exposes,' and it specifies the return content (units and ranges) beyond the schema. Baseline 3 is exceeded due to extra context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns 'the complete list of signals available from the currently connected data source, with units and typical ranges.' It uses a specific verb ('list') and resource ('signals'), and the context distinguishes it from siblings like 'get_signal' which fetches specific signals.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly advises: 'Call this FIRST whenever you are unsure whether a signal exists.' It provides clear rules: if a signal is not in the list, do not attempt to retrieve, estimate, or substitute, and inform the user of unavailability and the connected source. This offers excellent when-to-use and what-to-do guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_diagnostic_rulesA
Runs the domain diagnostic rule set over a time range and returns structured findings, each citing the specific data samples that support it.
Every finding includes evidence — the actual samples the rule examined — and a confidence level. A finding with confidence 'low' usually means the rule ran against insufficient data (for example, a timing rule given a single-sample point read). Report low-confidence findings as tentative; never present them as established fact.
Rules requiring signals the current source cannot provide are SKIPPED, not failed. Check skipped before concluding that no problems exist: an empty findings list with a non-empty skipped list means 'we didn't look,' not 'nothing is wrong.'
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | ||
| start | Yes | ||
| rule_ids | No | Specific rules to run. Omit to run all rules applicable to the available signals. Rules whose required signals are unavailable are skipped and reported in `skipped`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses that rules requiring unavailable signals are skipped (not failed), explains confidence levels, and notes that findings include evidence. No side effects are mentioned but the tool likely is read-only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with two focused paragraphs. First paragraph states purpose and key features, second provides usage guidance. Every sentence adds value with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 3 parameters and no output schema, the description explains the output structure (findings with evidence and confidence) and behavior (skipped rules). It covers interpretation needs well, though more detail on output fields would be beneficial.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 33% (only rule_ids has a description). The description adds context for rule_ids (skipped behavior) but does not elaborate on start and end parameters beyond the time range concept. Baseline is lowered due to low coverage, but the added value is marginal for the required params.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('Runs'), the resource ('domain diagnostic rule set'), and the output ('structured findings, each citing the specific data samples'). This distinguishes it from sibling tools like get_signal or list_available_signals.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit guidance on interpreting low-confidence findings and checking the skipped list before concluding no problems exist. It does not explicitly compare to siblings but offers strong usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
summarize_sessionA
Returns a structural overview of a data session: which signals are present, how many samples each has, where there are gaps in coverage, and a count of findings by severity.
Use this BEFORE detailed analysis to understand what data actually exists. This tool returns no interpretation — only structure. It will not tell you what a finding means; call run_diagnostic_rules for that.
coverage_gaps matters: a signal can be 'present' while missing the exact interval a user is asking about.
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | ||
| start | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that it returns only structure, not interpretation, and that coverage gaps matter. No annotations are present, so the description carries the full burden. It could mention that the tool is read-only and safe to call repeatedly, but overall it's transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise, front-loaded sentences. Each sentence provides distinct value: output description, usage guidance, and a critical warning. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema and no annotations, the description covers purpose, usage, and a behavioral caveat. However, it lacks parameter details and does not hint at the output format or examples. Adequate but leaves some gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has two required parameters (start, end) with no description for how to format them or what range they cover. Schema description coverage is 0%, and the description does not add any semantic meaning for the parameters. This is a significant gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns a structural overview of a data session, listing specific elements (signals, samples, gaps, findings by severity). It distinguishes itself from sibling tools like run_diagnostic_rules and list_available_signals.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly advises to use 'BEFORE detailed analysis' and directs to call run_diagnostic_rules for interpretation. Also warns about coverage_gaps being meaningful. Provides clear when-to-use and alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
4 tool updates
v0.1.0- First observed
get_signal - First observed
list_available_signals - First observed
run_diagnostic_rules - First observed
summarize_session
TDQS
Each tool has a clearly distinct purpose: listing available signals, retrieving specific signal data, running diagnostic rules, and summarizing session structure. There is no overlap or ambiguity.
All tool names follow a consistent verb_noun pattern (list_available_signals, get_signal, run_diagnostic_rules, summarize_session) making them predictable and easy to differentiate.
With 4 tools, the set is well-scoped for the domain of signal analysis and diagnostics. Each tool serves a necessary function without unnecessary bloat or missing essential operations.
The tools cover the core workflow: listing, retrieving, analyzing, and summarizing. Minor gaps exist, such as no tool for changing data sources or exporting results, but the core analysis loop is complete.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for building and testing AI agents with multi-model experimentation and insights.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA production-grade MCP server designed for multi-tenant, authenticated, and observable AI agent systems, enabling secure tool execution across heterogeneous data sources.62MIT
- AlicenseNot gradedqualityDmaintenanceA LangGraph-powered MCP server for infrastructure orchestration with autonomous subagents.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceMCP server implementing clean architecture with LangGraph for building and managing agent workflows.-
- FlicenseNot gradedqualityCmaintenanceA production-grade MCP server with 6 sandboxed tools and an agent orchestration engine for autonomous task completion, featuring an evaluation suite with CI/CD quality gates.-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kruslim/canopy'
If you have feedback or need assistance with the MCP directory API, please join our Discord server