diff --git a/.agents/skills/add-integration/SKILL.md b/.agents/skills/add-integration/SKILL.md index 5e489d61..517a0f0a 100644 --- a/.agents/skills/add-integration/SKILL.md +++ b/.agents/skills/add-integration/SKILL.md @@ -115,7 +115,17 @@ that your tools surface for natural-language queries users would actually type. Add an OAuth flow when the API supports it *and* you want guided credential setup in the Web UI. Get basic token auth working first. Grant type depends on the API: Device Flow for headless, PKCE for browser-redirect. Add a corresponding setup page in `web/templates/pages/`. -## 3. Implementation +## 3. Tool Definition Authoring (YAML-first) + +Tool definitions live in `integrations//tools.yaml`. See [`docs/tool-yaml.md`](../../../docs/tool-yaml.md) for schema, wiring, and strict-key rules. + +When adding a new adapter: +- Author `tools.yaml` directly — no inline `[]mcp.ToolDefinition{...}` literals. +- Skip the `//go:build parity` test pattern — it exists only for adapters being *migrated* from inline Go literals; a new adapter writing one would be testing nothing. + +The strict-mode loader (`KnownFields(true)`) panics at startup on key typos like `descripton:` instead of silently producing blank prose. Prose-only YAML edits also avoid Go-review noise. + +## 4. Implementation Reference `AGENTS.md > Adding a New Integration` for the 7-step mechanical checklist. Focus here on judgment calls: diff --git a/.agents/skills/optimize-integration/SKILL.md b/.agents/skills/optimize-integration/SKILL.md index b9f61ee3..10006d18 100644 --- a/.agents/skills/optimize-integration/SKILL.md +++ b/.agents/skills/optimize-integration/SKILL.md @@ -72,6 +72,17 @@ Focus on parameters where the wrong value is a common mistake: - Parameters that are mutually exclusive with another parameter - Parameters where a prerequisite tool provides the needed information +### Editing tool descriptions (YAML-first) + +Tool descriptions live in `integrations//tools.yaml`, not in Go source. To refine a description: + +1. Edit `tools.yaml` directly — no Go code changes needed for prose-only updates. +2. Run `go test ./integrations//...` to confirm the YAML parses without error. +3. Run the benchmark (see `/mcp-benchmark` or `/search-benchmark`) to measure the improvement. +4. Commit the YAML change alone — prose updates don't need a Go code review. + +The embed rebuild happens automatically at compile time via `//go:embed`. No separate codegen step. + ### Verification - `make ci` passes (descriptions are data, not logic) @@ -205,5 +216,4 @@ Use `"-*_url"` to exclude all fields matching a glob pattern. Valid in exclusion | Adding every field "just in case" | Every field costs tokens x N items — unjustified fields compound across pagination | Justify each field by the query it enables | | Enriching Tier 4 tools that are already clear | Description churn with no routing improvement | Don't touch what doesn't need touching | | Broad glob exclusions like `-*_url` | Silently excludes future upstream API fields that match the glob | Use targeted exclusions when field set is small and stable | -| Aliasing `ToolDefinition.Parameters` map in search | Progressive silent corruption — `extractSharedParameters` deletes from shared map | Always deep-copy the Parameters map when building `searchToolInfo` | | Mutation handler hardcodes one parent type when API has polymorphic parents | 400/silent failure for other parent types (e.g., `listAfter` on a collection ID in Notion) | Branch on parent type at the op-building layer; verify API uses the same write mechanism for all parent types | diff --git a/AGENTS.md b/AGENTS.md index 5d8fce4e..d785e252 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,6 +27,10 @@ 5. **MCP smoke test** — `TestSmoke_SearchResponseShape` in `server/server_test.go` validates the full response contract 6. **Go files must be `gofmt`'d** — run `make fmt` or `gofmt -w ` after editing `.go` files +## Testing Against the Live Server + +Before benchmarking or running tool-call tests against a running switchboard, verify the binary you're hitting matches the build you intend to test. Check `serverInfo.version` in the MCP `initialize` response (or `./dist/switchboard -version`) and compare against `git rev-parse --short HEAD`. Silent results from an old running binary look identical to results from the fresh build, so an unverified test pass means nothing (May 2026: a 30-minute benchmark ran against a stale `e94882e` binary from another branch before the mismatch was caught). + ## Git Workflow - Branch from `main` for all changes diff --git a/docs/prompts.md b/docs/prompts.md new file mode 100644 index 00000000..b4ebebb4 --- /dev/null +++ b/docs/prompts.md @@ -0,0 +1,358 @@ +# Prompts + +`server/prompts/` owns all LLM-facing prose in this repo — meta-tool descriptions and +runtime-assembled messages. Everything in the package is authored in `.md.tmpl` files and +accessed through typed Go methods. This doc covers where files live, naming rules, body +conventions, and how to add new accessors. + +--- + +## Where Prompts Live + +Two directories, two lifetimes: + +| Directory | Content | Rendered when | +|-----------|---------|---------------| +| `server/prompts/meta/` | Meta-tool descriptions: `search`, `execute`, `session`, `history`, `pin` | Once at server startup; passed as `Tool.Description` to the MCP SDK | +| `server/prompts/dynamic/` | Runtime messages: search-result summaries, circuit-breaker errors, response-too-large hints | Per-request, from dynamic wrappers in `wrappers.go` | + +Meta descriptions are fixed at registration time. The MCP Go SDK registers tools globally at +boot and returns identical descriptions for every session — one server process, one variant. +(See [Variant story](#variant-story-operational-vs-per-session) for what changes this.) + +--- + +## File and Accessor Naming + +### Meta accessors + +File: `server/prompts/meta/.md.tmpl` +Go method: `Meta.Accessor()` (PascalCase, on `metaAccessors`) + +| File | Method | +|------|--------| +| `meta/search.md.tmpl` | `prompts.Meta.Search()` | +| `meta/execute.md.tmpl` | `prompts.Meta.Execute()` | +| `meta/session.md.tmpl` | `prompts.Meta.Session()` | +| `meta/history.md.tmpl` | `prompts.Meta.History()` | +| `meta/pin.md.tmpl` | `prompts.Meta.Pin()` | + +### Dynamic wrappers + +File: `server/prompts/dynamic/.md.tmpl` +Go function: `SnakeCaseAsCamel(ctx Context, ...)` in `wrappers.go` + +| File | Function | +|------|----------| +| `dynamic/search_summary.md.tmpl` | `SearchSummary(ctx, total, query)` | +| `dynamic/circuit_breaker.md.tmpl` | `CircuitBreaker(ctx, integration, cooldownSeconds)` | +| `dynamic/response_too_large_hint.md.tmpl` | `ResponseTooLargeHint(ctx)` | + +### Why `.md.tmpl` everywhere + +Every file is parsed by `text/template` at init — even files whose v1 body is pure markdown +with no `<% %>` directives. The extension signals this uniformly. When a future variant adds +a directive, callers see no change; there is no engine-swap moment. + +--- + +## Template Body Conventions + +### Line 1: trim-marker header comment + +Every template starts with: + +``` +<%- /* filename.md.tmpl — v1, no variants */ -%> +``` + +The `-` trim markers on both sides strip surrounding whitespace. Without them the comment +emits a leading newline before the description body — visible as a stray blank line at the +top of every rendered description. + +### Custom delimiters `<%` `%>` + +Both parsers (`metaTmpl`, `dynamicTmpl`) use `<%` `%>` instead of the default `{{` `}}`. + +Rationale: `execute.md.tmpl` contains nine `}}` sequences inside JSON examples (e.g., +`{"arguments": {"owner": "x"}}`). With default delimiters the template parser panics at +init. Custom delimiters eliminate the collision permanently and apply to all files in both +directories — no per-file opt-in needed. + +**Never use `{{` `}}` in any file under `server/prompts/`.** + +### Trailing newline + +Markdown files end with `\n` by editor default. The original backtick strings in `server.go` +did not. The `render()` helper in `embed.go` calls `strings.TrimRight(out, "\n")` so every +accessor returns a string that matches the Go literal equivalent byte-for-byte. + +Do not manually strip the trailing newline from template files — `render()` handles it. + +### Total failure mode + +`template.Must` in `embed.go` wraps `ParseFS` for both parsers. If a template file is +missing or malformed, the process panics at **init**, not at the first call to an accessor. +This keeps the failure loud and immediate — a mis-named file surfaces in the first test run, +not in production on the first request that happens to call that accessor. + +Every accessor always returns a `string`. The function signature is total. There is no +"returns an error on missing file" path — the seam is enforced at startup. + +--- + +## Adding a New Meta-Tool Description + +Use `summarize` as a hypothetical example. + +**Step 1 — Add the accessor stub in `meta.go`:** + +```go +func (metaAccessors) Summarize() string { + return render(metaTmpl, "summarize.md.tmpl", nil) +} +``` + +**Step 2 — Create the template file:** + +``` +server/prompts/meta/summarize.md.tmpl +``` + +``` +<%- /* summarize.md.tmpl — v1, no variants */ -%> +Summarize tool results or documents into a compact digest. + +[prose body here] +``` + +**Step 3 — Register the accessor in the trim-newline test:** + +Add one line to `TestRender_TrimsTrailingNewline`'s cases table in `render_test.go`: + +```go +{"Meta.Summarize", Meta.Summarize}, +``` + +That table-driven test covers the new accessor's two routine failure modes — typo'd template +name (would panic at first call), and stray trailing newline (would diverge from a Go literal +at the wire boundary). No per-accessor test file is needed; the package logic is the same +for every meta-tool. + +Avoid writing per-template byte-identity tests (`require.Equal(t, raw_string, accessor())`). +With pure-markdown templates and no `<% %>` directives, those assert "string == string" — +the template body is duplicated into a Go constant that has to be edited in lockstep on +every prose change. Reach for them only when a template grows a directive (real logic to +exercise) or when variant selection lands. + +**Step 4 — Wire the call site in `server.go`:** + +```go +summarizeTool := &mcpsdk.Tool{ + Name: "summarize", + Description: prompts.Meta.Summarize(), + InputSchema: objectSchema(map[string]any{ /* ... */ }, nil), +} +``` + +### Parse-don't-validate at the call site + +`prompts.Meta.Summarize()` is the parse boundary for the accessor name. A typo in the +method name is a compile error. The alternative — `prompts.Meta.Get("summarize")` — pushes +validity to runtime: a mis-spelled string compiles fine, fails at first call with "template +not found." Typed methods eliminate that class of error entirely. + +The struct-with-methods shape also serves namespacing: `prompts.Meta.` in IDE autocomplete +lists exactly the five (or six, or N) meta-tools. `prompts.MetaSummarize()` as a free +function works but doesn't group. + +--- + +## Adding a New Dynamic-Prose Accessor + +Dynamic accessors mirror meta accessors but live in `wrappers.go` and `dynamic/`. + +**Differences from meta:** + +- First parameter is always `ctx Context`. The struct is currently empty and reserved for + future per-client variants. Pass it through to the template data even if unused. +- Additional runtime parameters follow `ctx` for values known only at call time (e.g., + `integration string`, `cooldownSeconds int` in `CircuitBreaker`). +- The template data struct is defined inline in the wrapper function — no named type needed + for call-site clarity. + +**Example — `CircuitBreaker`:** + +```go +func CircuitBreaker(ctx Context, integration string, cooldownSeconds int) string { + return render(dynamicTmpl, "circuit_breaker.md.tmpl", struct { + Ctx Context + Integration string + CooldownSeconds int + }{ctx, integration, cooldownSeconds}) +} +``` + +Template fields are accessed as `<% .Integration %>`, `<% .CooldownSeconds %>`. + +Testing for dynamic wrappers differs from meta accessors — dynamic templates DO carry +directives (parameter interpolation, conditional branches), so per-wrapper tests in +`prompts_test.go` exercise real logic: that `printf "%q"` quoting matches `fmt.Sprintf`'s +quoting byte-exact, that conditional branches produce the right output for each input case, +that table-driven inputs cover empty/unicode/quoted edge cases. Mirror the existing tests +for `SearchSummary` and `CircuitBreaker` when adding a new dynamic wrapper. + +--- + +## Variant Story: Operational vs Per-Session + +There are two distinct paths to per-client variants. They have different scope and different +prerequisites. + +### Path 1 — Operational variants (in scope, not v1) + +Server reads an env var at startup (e.g., `SWITCHBOARD_CLIENT_FAMILY=claude-code`). A +`renderMeta` helper selects between sibling template files: + +``` +server/prompts/meta/ +├── execute.md.tmpl # default (always present) +├── execute.claude-code.md.tmpl # operational variant +└── execute.cursor.md.tmpl +``` + +One variant per server process. Deploy one process per client family. No SDK changes +required. Accessor signatures stay `() string` — the variant selection is internal to +`render` or `renderMeta`. + +### Path 2 — Per-session variants (out of scope, requires SDK work) + +Requires `modelcontextprotocol/go-sdk` to expose a `ListToolsHandler` (or equivalent) in +`ServerOptions` so each `tools/list` response can vary by session metadata. Today the SDK +registers tools globally at boot; `listTools` returns identical descriptions for every +session. + +Two sub-paths: upstream a PR to the SDK, or maintain a fork. Open SDK issues #666 and #745 +point in this direction but are "needs investigation." Neither sub-path is in scope for v1. + +### What changes when Path 2 lands + +Accessor signatures widen from `Meta.Execute() string` to `Meta.Execute(Context) string`. +All five call sites in `server.go` update simultaneously to pass a `Context` built from +session state. Any other consumer of `Tool.Description` (search indexes, telemetry, etc.) +must be touched too — easy to miss in a "small migration" claim. The SDK work is a separate +prerequisite effort. + +This migration is bounded but not trivial. v1 defers it intentionally — there is no +concrete per-session need yet. + +--- + +## Constraint-Driven Framing (for Prose Authors) + +Every instruction in a meta-tool description should name the failure it prevents, not just +assert a rule. The mechanism produces compliance; the megaphone produces resentment — and +models ignore both equally when the reason is absent. + +**Before:** +``` +IMPORTANT: Always search before calling execute. Do NOT guess tool names. +``` + +**After:** +``` +Tool names shift across integration versions. A guessed name returns "tool not found" with +no fallback — search first to get the live name. +``` + +**Before:** +``` +PREFER scripts when a task requires 2+ tool calls or crosses integrations +``` + +**After:** +``` +Two or more tool calls without a script means each intermediate result lands in the +conversation context — usually 5–10x the bytes you actually need. Use scripts to keep +intermediates server-side. +``` + +Rule: **every instruction names the failure it prevents**. Reach for the mechanism, not the +megaphone. + +--- + +## Counterweights + +When you write a one-direction nudge, include the opposite case. Without it the model +over-applies the rule: + +> PREFER scripts when a task requires 2+ tool calls or crosses integrations. +> For two calls where the second doesn't read from the first, call directly — script +> overhead isn't worth it. + +Counterweights without removal triggers become permanent cruft. When telemetry can measure +the inversion rate (e.g., "user corrects script to direct call N% of the time"), document a +removal threshold in the commit body so a future author knows when to simplify. + +--- + +## Anthropic Prompt-Cache Discipline + +Meta-tool descriptions register at server boot and don't change per session. Anthropic API +clients that cache tool registrations pay the token cost once at startup — educational +content, worked examples, and counterweights in `meta/*.md.tmpl` are amortized across the +session. + +Verify with downstream clients before assuming generosity is free. If a client doesn't cache +tool registrations, the full description is paid per-request, and the description needs +trimming. This argues for keeping `meta/*.md.tmpl` bodies generous on examples by default +and leaning them out only where measurement shows runaway context use. + +--- + +## Deferred Audit Items + +### Discovery rule duplication (Workstream A item 1) + +The "always search before execute," session-context, and auto-pin protocols appear in both +`ServerOptions.Instructions` (server.go) and the individual meta-tool descriptions. Every +session reads the same rule twice. + +**Status:** deferred to a follow-up workstream. + +**Why deferred:** picking between Option A (consolidate to `Instructions`, slim descriptions) +and Option B (remove `Instructions`, descriptions stand alone) requires measurement. Not all +MCP clients forward `Instructions` to the model — some strip or summarize it. Without +telemetry on client behavior, choosing A risks losing the rule for clients that drop +`Instructions`; choosing B leaves a per-session token cost on every description. Pick one +when measurement is available. + +### MCP Resources for execute's JSON examples (Workstream A item 5) + +The seven worked JSON examples in `execute.md.tmpl` account for ~30% of its tokens. +MCP Resources could deliver them on-demand instead of carrying them in every session prompt. + +**Status:** deferred to a follow-up workstream. + +**Why deferred:** the tradeoff is discoverability (gone if externalized — the model no longer +sees examples passively) vs context cost. Justified only if measurement shows examples bloat +real-workload context beyond an acceptable threshold. Land when a measurement target exists. + +### Downstream-client cache verification (Workstream A item 9) + +The Prompt-Cache Discipline section above assumes Anthropic API clients cache MCP tool +registrations between sessions. If they do, educational content in `meta/*.md.tmpl` is paid +once at startup and amortized; if they don't, every session pays the full description. + +**Status:** deferred. Requires inspecting downstream-client internals (Claude Code, Cursor, +others) or running an instrumented session to observe cache headers. + +**Question to answer:** for each downstream Anthropic-using MCP client, does the +`tools/list` response land in the prompt-cache prefix, or is it re-injected per request? + +**Implication if false:** the descriptions are paid per-session, and the generosity in +worked examples + counterweights becomes a measurable token cost. The first action would be +trimming `execute.md.tmpl`'s 7 JSON examples — which is exactly what item 5 (MCP Resources) +would land. The two deferrals share a trigger: if real-workload measurement shows description +cost is a problem, address them together. diff --git a/docs/tool-yaml.md b/docs/tool-yaml.md new file mode 100644 index 00000000..2c32e409 --- /dev/null +++ b/docs/tool-yaml.md @@ -0,0 +1,147 @@ +# tool-yaml.md — Tool Definition Authoring Reference + +Each adapter ships a `tools.yaml` file with its tool descriptions and parameter specs. The Go side embeds the YAML at compile time and parses it into typed values once at startup. Downstream code never sees the raw YAML. It works with the typed `[]ToolDefinition`. + +This doc covers the schema, the rules, and the migration pattern. + +## File Location + +``` +integrations//tools.yaml +integrations//tools.go # 3-line embed-and-load; no inline literals +``` + +Every adapter also has a `compact.yaml` next to its `tools.yaml`. They sit next to each other but they answer different questions. `tools.yaml` is what the model sees when it lists or executes a tool. `compact.yaml` controls which fields come back from an API call. Don't merge them. + +## Schema + +```yaml +version: 1 +tools: + : + description: "" + parameters: + : + description: "" + required: true # only `true` is permitted; absence means optional + : + description: "<...>" +``` + +### Fields + +| Field | Type | Required | Notes | +|-------|------|----------|-------| +| `version` | int | yes | Must be `1` | +| `tools` | map | yes | Keyed by fully-prefixed tool name | +| `tools..description` | string | yes | The tool's LLM-facing description | +| `tools..parameters` | map | no | Absent or `{}` means no parameters | +| `tools..parameters.

.description` | string | yes (if param present) | Parameter's LLM-facing description | +| `tools..parameters.

.required` | bool | no | Only `true` is permitted (required parameter). Absence means optional. `required: false` is rejected as a parse error — it is redundant with absence. | + +### Tool naming + +Tool names must be fully prefixed with the integration name: `github_list_issues`, `datadog_search_logs`. This matches the `AGENTS.md` naming convention and the dispatch map keys. + +### Example + +```yaml +version: 1 +tools: + myapi_list_items: + description: "List items in the project. Start here for item discovery and triage." + parameters: + project_id: + description: "The project ID (from myapi_list_projects)." + required: true + limit: + description: "Maximum items to return (default 50, max 200)." + myapi_get_item: + description: "Get a single item by ID. Use after myapi_list_items." + parameters: + item_id: + description: "The item ID (from myapi_list_items)." + required: true +``` + +## Strict-Mode Validation + +The loader rejects unknown keys at every nesting level. A typo like `descripton:` instead of `description:` fails the load. So does `requird: true` inside a parameter block, or any other key the schema doesn't recognize. + +`MustLoadToolsYAML` panics on failure, so the server crashes at startup if any adapter ships a malformed `tools.yaml`. We chose loud-fail-at-init over silent-blank-prose-to-the-model. + +The same strictness applies to the values that key names map to. `required: false` is rejected. Absence already conveys optional, so the explicit `false` is redundant and the author should omit the key. The error message tells them so. + +## Parameter Declaration Order + +Whatever order you write parameters in YAML is the order they come back from the loader, the order the dispatch sees, and the order that shows up in the wire output. The loader walks the YAML's underlying node tree directly so iteration is deterministic; Go's map iteration would have randomized it. + +This matters because the model reads parameter lists top to bottom. Put the most important parameter first. + +## Wire-Format Normalization + +One thing the loader doesn't preserve verbatim: the `required: [...]` array in each tool's `inputSchema`. We sort that array alphabetically at the wire boundary before emitting it. JSON Schema treats `required` as a set, so sort order carries no meaning to the consumer. + +The reason we sort is mechanical, not semantic. With sorted output we can lock the live wire response and compare against it byte-for-byte. That lock lives at `server/tools_list.lock.json`, and the test that enforces it is `TestToolsList_MatchesWireLock` in `server/wire_test.go`. It is a lock file in the same sense as `go.sum`: a generated artifact derived from the `tools.yaml` files, committed so CI can catch drift. You don't hand-edit it. After an intentional change to a tool's name, description, parameters, or required flags, regenerate it with `go test ./server -run TestToolsList_MatchesWireLock -update` and commit the updated lock alongside the YAML change. As long as that test stays green, nothing has silently changed what the model sees. + +## tools.go Wiring + +Every adapter's `tools.go` is a 3-line file: + +```go +package myapi + +import ( + _ "embed" + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) +``` + +`//go:embed` bundles the YAML into the binary at compile time. One build step covers both the Go and the YAML, so prose-only updates to `tools.yaml` rebuild the same way code changes do. + +## Parity Test Pattern (Migration Only) + +When we migrate an existing adapter from inline Go literals to YAML, we copy the old definitions verbatim into a `legacyTools` fixture and assert the YAML-loaded `tools` equals it. This is the rope that catches conversion mistakes: silent prose loss, dropped parameters, wrong required flag. + +New adapters starting YAML-first skip this. There's nothing to compare against. + +```go +//go:build parity + +package myapi + +import ( + "testing" + mcp "github.com/daltoniam/switchboard" + "github.com/stretchr/testify/require" +) + +// legacyTools is a verbatim snapshot of the pre-YAML inline definitions. +// Removed in Phase 3 cleanup after all adapters migrate. +var legacyTools = []mcp.ToolDefinition{ + { + Name: mcp.ToolName("myapi_list_items"), + Description: "", + Parameters: []mcp.Parameter{ + {Name: mcp.ParamName("project_id"), Description: "", Required: true}, + }, + }, +} + +func TestToolsYAML_ParityWithLegacy(t *testing.T) { + require.Equal(t, legacyTools, tools) +} +``` + +Run with: `go test -tags parity ./integrations//...` + +The build tag keeps the `legacyTools` fixture out of normal builds. After every adapter has migrated, Phase 3 deletes these files in one sweep. + +## Variant Story + +The broader prompt-variant pattern (operational vs per-session, per-model tuning) lives in [`docs/prompts.md`](prompts.md). When tool description variants land, they'll follow the same approach. We kept `version: 1` at the top of every file so we can grow into structured variant extensions without breaking existing YAML. diff --git a/integrations/acp/acp_test.go b/integrations/acp/acp_test.go index 4d312f4f..17d280c3 100644 --- a/integrations/acp/acp_test.go +++ b/integrations/acp/acp_test.go @@ -102,8 +102,14 @@ func TestTools_NoDuplicateNames(t *testing.T) { func TestTools_AllHaveServerURLParam(t *testing.T) { i := New() for _, tool := range i.Tools() { - _, ok := tool.Parameters["server_url"] - assert.True(t, ok, "tool %s missing server_url parameter", tool.Name) + found := false + for _, p := range tool.Parameters { + if p.Name == "server_url" { + found = true + break + } + } + assert.True(t, found, "tool %s missing server_url parameter", tool.Name) } } diff --git a/integrations/acp/tools.go b/integrations/acp/tools.go index 0bb715ed..8024d667 100644 --- a/integrations/acp/tools.go +++ b/integrations/acp/tools.go @@ -1,40 +1,12 @@ package acp -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - { - Name: mcp.ToolName("acp_list_agents"), - Description: "List available remote agents on ACP servers. Start here to discover AI agents, bots, and autonomous workers before invoking them. Returns agent names, descriptions, and capabilities", - Parameters: map[string]string{ - "server": "Name of a pre-configured ACP server to query. Uses the first configured server if omitted", - "server_url": "URL of an ACP server to query directly (e.g. http://localhost:8199). Overrides server name lookup", - "server_headers": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})", - }, - }, - { - Name: mcp.ToolName("acp_run_agent"), - Description: "Invoke a remote ACP agent with a message and get its response. Send a text prompt to an AI agent on a remote server. Use acp_list_agents first to discover available agents. If the agent enters an awaiting state (needs more input), the response includes a run_id — use acp_resume_run to continue", - Parameters: map[string]string{ - "agent_name": "Name of the remote agent to invoke", - "input": "Text message to send to the agent", - "server": "Name of a pre-configured ACP server. Uses the first configured server if omitted", - "server_url": "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup", - "server_headers": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})", - "session_id": "Session ID for multi-turn conversations with the same agent", - }, - Required: []string{"agent_name", "input"}, - }, - { - Name: mcp.ToolName("acp_resume_run"), - Description: "Resume an ACP agent run that is waiting for additional input. When acp_run_agent returns a response indicating the agent is awaiting input, use this tool to provide the requested information and continue the run", - Parameters: map[string]string{ - "run_id": "The run_id returned by acp_run_agent when the agent entered awaiting state", - "input": "Text response to provide to the awaiting agent", - "server": "Name of a pre-configured ACP server. Uses the first configured server if omitted", - "server_url": "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup", - "server_headers": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})", - }, - Required: []string{"run_id", "input"}, - }, -} + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/acp/tools.yaml b/integrations/acp/tools.yaml new file mode 100644 index 00000000..78710ab5 --- /dev/null +++ b/integrations/acp/tools.yaml @@ -0,0 +1,43 @@ +version: 1 +tools: + acp_list_agents: + description: "List available remote agents on ACP servers. Start here to discover AI agents, bots, and autonomous workers before invoking them. Returns agent names, descriptions, and capabilities" + parameters: + server: + description: "Name of a pre-configured ACP server to query. Uses the first configured server if omitted" + server_url: + description: "URL of an ACP server to query directly (e.g. http://localhost:8199). Overrides server name lookup" + server_headers: + description: 'Optional JSON object of HTTP headers to send with the request (e.g. {"Authorization":"Bearer sk-xxx"})' + acp_run_agent: + description: "Invoke a remote ACP agent with a message and get its response. Send a text prompt to an AI agent on a remote server. Use acp_list_agents first to discover available agents. If the agent enters an awaiting state (needs more input), the response includes a run_id — use acp_resume_run to continue" + parameters: + agent_name: + description: "Name of the remote agent to invoke" + required: true + input: + description: "Text message to send to the agent" + required: true + server: + description: "Name of a pre-configured ACP server. Uses the first configured server if omitted" + server_url: + description: "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup" + server_headers: + description: 'Optional JSON object of HTTP headers to send with the request (e.g. {"Authorization":"Bearer sk-xxx"})' + session_id: + description: "Session ID for multi-turn conversations with the same agent" + acp_resume_run: + description: "Resume an ACP agent run that is waiting for additional input. When acp_run_agent returns a response indicating the agent is awaiting input, use this tool to provide the requested information and continue the run" + parameters: + run_id: + description: "The run_id returned by acp_run_agent when the agent entered awaiting state" + required: true + input: + description: "Text response to provide to the awaiting agent" + required: true + server: + description: "Name of a pre-configured ACP server. Uses the first configured server if omitted" + server_url: + description: "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup" + server_headers: + description: 'Optional JSON object of HTTP headers to send with the request (e.g. {"Authorization":"Bearer sk-xxx"})' diff --git a/integrations/agents/tools.go b/integrations/agents/tools.go index fb9d7bcb..3d17233b 100644 --- a/integrations/agents/tools.go +++ b/integrations/agents/tools.go @@ -1,230 +1,12 @@ package agents -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ========================================================================= - // ProjectService — gRPC via h2c - // ========================================================================= - { - Name: "agents_project_list", - Description: "List all registered projects via gRPC ProjectService.ListProjects. Returns Project objects with name, repo, branch, and agent templates.", - Parameters: map[string]string{}, - }, - { - Name: "agents_project_register", - Description: "Register a new project via gRPC ProjectService.RegisterProject. A project defines a git repo and agent templates for spawning.", - Parameters: map[string]string{ - "name": "Unique project name", - "repo": "Path or URL to the git repository", - "branch": "Default branch for new workspaces (default: main)", - "agents": `JSON array of AgentTemplate objects. Each has: name (required), command (required), port_env, capabilities (string array), a2a_card_config ({name, description, skills, input_modes, output_modes, streaming})`, - }, - Required: []string{"name", "repo"}, - }, - { - Name: "agents_project_unregister", - Description: "Unregister a project via gRPC ProjectService.UnregisterProject. Fails if active workspaces with running agents exist.", - Parameters: map[string]string{ - "name": "Project name to unregister", - }, - Required: []string{"name"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ========================================================================= - // WorkspaceService — gRPC via h2c - // ========================================================================= - { - Name: "agents_workspace_create", - Description: "Create a new workspace via gRPC WorkspaceService.CreateWorkspace. Creates a git worktree and optionally auto-spawns agents.", - Parameters: map[string]string{ - "name": "Unique workspace name (used as worktree branch name)", - "project": "Project to create workspace from (must be registered)", - "branch": "Git branch override (defaults to project's default branch)", - "auto_agents": `JSON array of template names to auto-spawn (e.g. ["crush", "reviewer"])`, - }, - Required: []string{"name", "project"}, - }, - { - Name: "agents_workspace_list", - Description: "List workspaces via gRPC WorkspaceService.ListWorkspaces. Optionally filter by project or status.", - Parameters: map[string]string{ - "project": "Filter by project name", - "status": "Filter by status: active or inactive", - }, - }, - { - Name: "agents_workspace_get", - Description: "Get workspace details via gRPC WorkspaceService.GetWorkspace. Returns agents, directory path, and creation time.", - Parameters: map[string]string{ - "name": "Workspace name", - }, - Required: []string{"name"}, - }, - { - Name: "agents_workspace_destroy", - Description: "Destroy a workspace via gRPC WorkspaceService.DestroyWorkspace. Stops all agents, cancels working tasks, and optionally removes the worktree.", - Parameters: map[string]string{ - "name": "Workspace name to destroy", - "keep_worktree": "If true, preserve the git worktree directory on disk (default: false)", - }, - Required: []string{"name"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ========================================================================= - // AgentService — gRPC via h2c (lifecycle) - // ========================================================================= - { - Name: "agents_agent_spawn", - Description: "Spawn a new A2A agent via gRPC AgentService.SpawnAgent. Returns AgentInstance with id, port, direct_url, proxy_url, and status.", - Parameters: map[string]string{ - "workspace": "Workspace name to spawn the agent in", - "template": "Agent template name from the project", - "name": "Custom instance name (defaults to template name)", - "env": "JSON object of additional environment variables", - "prompt": "Initial prompt to send after agent reaches READY", - "scope": `JSON Scope object: {"global": false, "projects": ["proj-a"]}`, - "permission": "Permission level: session, project, or admin", - }, - Required: []string{"workspace", "template"}, - }, - { - Name: "agents_agent_list", - Description: "List agent instances via gRPC AgentService.ListAgents. Optionally filter by workspace, status, or template.", - Parameters: map[string]string{ - "workspace": "Filter by workspace name", - "status": "Filter by status: starting, ready, busy, error, stopping, stopped", - "template": "Filter by template name", - }, - }, - { - Name: "agents_agent_status", - Description: "Get agent status via gRPC AgentService.GetAgentStatus. Returns AgentInstance with resolved A2A AgentCard.", - Parameters: map[string]string{ - "agent_id": "Agent instance ID", - }, - Required: []string{"agent_id"}, - }, - { - Name: "agents_agent_stop", - Description: "Stop an agent via gRPC AgentService.StopAgent. Cancels working A2A tasks, sends SIGTERM, waits grace period, then SIGKILL.", - Parameters: map[string]string{ - "agent_id": "Agent instance ID to stop", - "grace_period_ms": "Milliseconds to wait after SIGTERM before SIGKILL (default: 5000)", - }, - Required: []string{"agent_id"}, - }, - { - Name: "agents_agent_restart", - Description: "Restart an agent via gRPC AgentService.RestartAgent. Stop + spawn with same config. proxy_url stays stable.", - Parameters: map[string]string{ - "agent_id": "Agent instance ID to restart", - }, - Required: []string{"agent_id"}, - }, - - // ========================================================================= - // AgentService — gRPC via h2c (messaging) - // ========================================================================= - { - Name: "agents_agent_message", - Description: "Send a message to an agent via gRPC AgentService.SendAgentMessage. When blocking=true (default), polls until the task completes. When blocking=false, returns the task immediately for async tracking via agents_agent_task_status.", - Parameters: map[string]string{ - "agent_id": "Agent instance ID", - "message": "Text message to send", - "context_id": "A2A context_id for multi-turn conversations", - "blocking": "If true (default), wait for response. If false, return immediately.", - }, - Required: []string{"agent_id", "message"}, - }, - { - Name: "agents_agent_task", - Description: "Create a task on an agent via gRPC AgentService.CreateAgentTask. Returns immediately with a Task for async tracking via agents_agent_task_status (never blocks on completion).", - Parameters: map[string]string{ - "agent_id": "Agent instance ID", - "message": "Task description", - "context_id": "A2A context_id to continue a conversation", - }, - Required: []string{"agent_id", "message"}, - }, - { - Name: "agents_agent_task_status", - Description: "Get task status via gRPC AgentService.GetAgentTaskStatus. Returns Task with status, artifacts, and history.", - Parameters: map[string]string{ - "agent_id": "Agent instance ID", - "task_id": "A2A task ID to check", - "history_length": "Maximum number of recent messages to include (default: 10)", - }, - Required: []string{"agent_id", "task_id"}, - }, - - // ========================================================================= - // DiscoveryService — gRPC via h2c - // ========================================================================= - { - Name: "agents_discover", - Description: "Discover agents via gRPC DiscoveryService.DiscoverAgents. Returns enriched AgentCards. Supports local/network scope and capability filtering.", - Parameters: map[string]string{ - "scope": `Discovery scope: local (default) or network`, - "capability": `Filter by AgentSkill tag (e.g. "coding")`, - "urls": `JSON array of base URLs to probe for AgentCards (network scope)`, - }, - }, - - // ========================================================================= - // A2A proxy — HTTP endpoints on separate port - // ========================================================================= - { - Name: "agents_proxy_list", - Description: "List all A2A AgentCards via the ARP HTTP proxy at /a2a/agents. Returns cards for READY/BUSY agents with metadata.arp fields.", - Parameters: map[string]string{}, - }, - { - Name: "agents_agent_card", - Description: "Get an enriched A2A AgentCard via the ARP HTTP proxy. Includes metadata.arp and supportedInterfaces pointing to the proxy.", - Parameters: map[string]string{ - "agent_id": "Agent ID, name, or workspace/instance_name", - }, - Required: []string{"agent_id"}, - }, - { - Name: "agents_proxy_send_message", - Description: "Send an A2A message through the ARP HTTP proxy at /a2a/agents/{id}/message:send. Routes by agent ID, name, or workspace/name.", - Parameters: map[string]string{ - "agent_id": "Agent ID, name, or workspace/instance_name", - "message": "Text message to send", - "context_id": "A2A context_id for multi-turn conversation", - "message_id": "Message ID (auto-generated if omitted)", - }, - Required: []string{"agent_id", "message"}, - }, - { - Name: "agents_proxy_get_task", - Description: "Get an A2A task via the ARP HTTP proxy.", - Parameters: map[string]string{ - "agent_id": "Agent ID", - "task_id": "A2A task ID", - }, - Required: []string{"agent_id", "task_id"}, - }, - { - Name: "agents_proxy_cancel_task", - Description: "Cancel an A2A task via the ARP HTTP proxy.", - Parameters: map[string]string{ - "agent_id": "Agent ID", - "task_id": "A2A task ID to cancel", - }, - Required: []string{"agent_id", "task_id"}, - }, - { - Name: "agents_route_message", - Description: "Route an A2A message by skill tags via the ARP HTTP proxy at /a2a/route/message:send. Finds best matching agent (prefers READY over BUSY).", - Parameters: map[string]string{ - "message": "Text message to send", - "tags": `JSON array of skill tags to match (e.g. ["coding"])`, - "context_id": "A2A context_id for multi-turn conversation", - "message_id": "Message ID (auto-generated if omitted)", - }, - Required: []string{"message", "tags"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/agents/tools.yaml b/integrations/agents/tools.yaml new file mode 100644 index 00000000..68cbf841 --- /dev/null +++ b/integrations/agents/tools.yaml @@ -0,0 +1,203 @@ +version: 1 +tools: + agents_project_list: + description: "List all registered projects via gRPC ProjectService.ListProjects. Returns Project objects with name, repo, branch, and agent templates." + parameters: {} + agents_project_register: + description: "Register a new project via gRPC ProjectService.RegisterProject. A project defines a git repo and agent templates for spawning." + parameters: + name: + description: "Unique project name" + required: true + repo: + description: "Path or URL to the git repository" + required: true + branch: + description: "Default branch for new workspaces (default: main)" + agents: + description: 'JSON array of AgentTemplate objects. Each has: name (required), command (required), port_env, capabilities (string array), a2a_card_config ({name, description, skills, input_modes, output_modes, streaming})' + agents_project_unregister: + description: "Unregister a project via gRPC ProjectService.UnregisterProject. Fails if active workspaces with running agents exist." + parameters: + name: + description: "Project name to unregister" + required: true + agents_workspace_create: + description: "Create a new workspace via gRPC WorkspaceService.CreateWorkspace. Creates a git worktree and optionally auto-spawns agents." + parameters: + name: + description: "Unique workspace name (used as worktree branch name)" + required: true + project: + description: "Project to create workspace from (must be registered)" + required: true + branch: + description: "Git branch override (defaults to project's default branch)" + auto_agents: + description: 'JSON array of template names to auto-spawn (e.g. ["crush", "reviewer"])' + agents_workspace_list: + description: "List workspaces via gRPC WorkspaceService.ListWorkspaces. Optionally filter by project or status." + parameters: + project: + description: "Filter by project name" + status: + description: "Filter by status: active or inactive" + agents_workspace_get: + description: "Get workspace details via gRPC WorkspaceService.GetWorkspace. Returns agents, directory path, and creation time." + parameters: + name: + description: "Workspace name" + required: true + agents_workspace_destroy: + description: "Destroy a workspace via gRPC WorkspaceService.DestroyWorkspace. Stops all agents, cancels working tasks, and optionally removes the worktree." + parameters: + name: + description: "Workspace name to destroy" + required: true + keep_worktree: + description: "If true, preserve the git worktree directory on disk (default: false)" + agents_agent_spawn: + description: "Spawn a new A2A agent via gRPC AgentService.SpawnAgent. Returns AgentInstance with id, port, direct_url, proxy_url, and status." + parameters: + workspace: + description: "Workspace name to spawn the agent in" + required: true + template: + description: "Agent template name from the project" + required: true + name: + description: "Custom instance name (defaults to template name)" + env: + description: "JSON object of additional environment variables" + prompt: + description: "Initial prompt to send after agent reaches READY" + scope: + description: 'JSON Scope object: {"global": false, "projects": ["proj-a"]}' + permission: + description: "Permission level: session, project, or admin" + agents_agent_list: + description: "List agent instances via gRPC AgentService.ListAgents. Optionally filter by workspace, status, or template." + parameters: + workspace: + description: "Filter by workspace name" + status: + description: "Filter by status: starting, ready, busy, error, stopping, stopped" + template: + description: "Filter by template name" + agents_agent_status: + description: "Get agent status via gRPC AgentService.GetAgentStatus. Returns AgentInstance with resolved A2A AgentCard." + parameters: + agent_id: + description: "Agent instance ID" + required: true + agents_agent_stop: + description: "Stop an agent via gRPC AgentService.StopAgent. Cancels working A2A tasks, sends SIGTERM, waits grace period, then SIGKILL." + parameters: + agent_id: + description: "Agent instance ID to stop" + required: true + grace_period_ms: + description: "Milliseconds to wait after SIGTERM before SIGKILL (default: 5000)" + agents_agent_restart: + description: "Restart an agent via gRPC AgentService.RestartAgent. Stop + spawn with same config. proxy_url stays stable." + parameters: + agent_id: + description: "Agent instance ID to restart" + required: true + agents_agent_message: + description: "Send a message to an agent via gRPC AgentService.SendAgentMessage. When blocking=true (default), polls until the task completes. When blocking=false, returns the task immediately for async tracking via agents_agent_task_status." + parameters: + agent_id: + description: "Agent instance ID" + required: true + message: + description: "Text message to send" + required: true + context_id: + description: "A2A context_id for multi-turn conversations" + blocking: + description: "If true (default), wait for response. If false, return immediately." + agents_agent_task: + description: "Create a task on an agent via gRPC AgentService.CreateAgentTask. Returns immediately with a Task for async tracking via agents_agent_task_status (never blocks on completion)." + parameters: + agent_id: + description: "Agent instance ID" + required: true + message: + description: "Task description" + required: true + context_id: + description: "A2A context_id to continue a conversation" + agents_agent_task_status: + description: "Get task status via gRPC AgentService.GetAgentTaskStatus. Returns Task with status, artifacts, and history." + parameters: + agent_id: + description: "Agent instance ID" + required: true + task_id: + description: "A2A task ID to check" + required: true + history_length: + description: "Maximum number of recent messages to include (default: 10)" + agents_discover: + description: "Discover agents via gRPC DiscoveryService.DiscoverAgents. Returns enriched AgentCards. Supports local/network scope and capability filtering." + parameters: + scope: + description: "Discovery scope: local (default) or network" + capability: + description: 'Filter by AgentSkill tag (e.g. "coding")' + urls: + description: "JSON array of base URLs to probe for AgentCards (network scope)" + agents_proxy_list: + description: "List all A2A AgentCards via the ARP HTTP proxy at /a2a/agents. Returns cards for READY/BUSY agents with metadata.arp fields." + parameters: {} + agents_agent_card: + description: "Get an enriched A2A AgentCard via the ARP HTTP proxy. Includes metadata.arp and supportedInterfaces pointing to the proxy." + parameters: + agent_id: + description: "Agent ID, name, or workspace/instance_name" + required: true + agents_proxy_send_message: + description: "Send an A2A message through the ARP HTTP proxy at /a2a/agents/{id}/message:send. Routes by agent ID, name, or workspace/name." + parameters: + agent_id: + description: "Agent ID, name, or workspace/instance_name" + required: true + message: + description: "Text message to send" + required: true + context_id: + description: "A2A context_id for multi-turn conversation" + message_id: + description: "Message ID (auto-generated if omitted)" + agents_proxy_get_task: + description: "Get an A2A task via the ARP HTTP proxy." + parameters: + agent_id: + description: "Agent ID" + required: true + task_id: + description: "A2A task ID" + required: true + agents_proxy_cancel_task: + description: "Cancel an A2A task via the ARP HTTP proxy." + parameters: + agent_id: + description: "Agent ID" + required: true + task_id: + description: "A2A task ID to cancel" + required: true + agents_route_message: + description: "Route an A2A message by skill tags via the ARP HTTP proxy at /a2a/route/message:send. Finds best matching agent (prefers READY over BUSY)." + parameters: + message: + description: "Text message to send" + required: true + tags: + description: 'JSON array of skill tags to match (e.g. ["coding"])' + required: true + context_id: + description: "A2A context_id for multi-turn conversation" + message_id: + description: "Message ID (auto-generated if omitted)" diff --git a/integrations/amazon/tools.go b/integrations/amazon/tools.go index 6fb93965..502be638 100644 --- a/integrations/amazon/tools.go +++ b/integrations/amazon/tools.go @@ -1,47 +1,12 @@ package amazon -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Products ────────────────────────────────────────────────── - { - Name: mcp.ToolName("amazon_search_products"), - Description: "Search for products on Amazon. Returns up to 20 results with title, price, rating, Prime eligibility, and ASIN. Start here for product discovery workflows.", - Parameters: map[string]string{ - "search_term": "Search query (e.g. 'wireless headphones', 'collagen powder')", - }, - Required: []string{"search_term"}, - }, - { - Name: mcp.ToolName("amazon_get_product"), - Description: "Get detailed product information by ASIN (Amazon Standard Identification Number, 10 characters). Returns title, price, description sections, reviews, and image URL. Use after amazon_search_products to drill into a specific product.", - Parameters: map[string]string{ - "asin": "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')", - }, - Required: []string{"asin"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Orders ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("amazon_get_orders"), - Description: "Get the authenticated user's recent order history. Returns order details including items, delivery address, status, and return eligibility. Requires valid session cookies.", - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Cart ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("amazon_get_cart"), - Description: "Get the current Amazon cart contents. Returns items with title, price, quantity, availability, and cart subtotal. Requires valid session cookies.", - }, - { - Name: mcp.ToolName("amazon_add_to_cart"), - Description: "Add a product to the Amazon cart by ASIN. Navigates to product page and submits the add-to-cart form. Requires valid session cookies.", - Parameters: map[string]string{ - "asin": "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')", - }, - Required: []string{"asin"}, - }, - { - Name: mcp.ToolName("amazon_clear_cart"), - Description: "Remove all items from the Amazon cart. Iterates through cart items and deletes each one. Requires valid session cookies.", - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/amazon/tools.yaml b/integrations/amazon/tools.yaml new file mode 100644 index 00000000..805a624d --- /dev/null +++ b/integrations/amazon/tools.yaml @@ -0,0 +1,26 @@ +version: 1 +tools: + amazon_search_products: + description: "Search for products on Amazon. Returns up to 20 results with title, price, rating, Prime eligibility, and ASIN. Start here for product discovery workflows." + parameters: + search_term: + description: "Search query (e.g. 'wireless headphones', 'collagen powder')" + required: true + amazon_get_product: + description: "Get detailed product information by ASIN (Amazon Standard Identification Number, 10 characters). Returns title, price, description sections, reviews, and image URL. Use after amazon_search_products to drill into a specific product." + parameters: + asin: + description: "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')" + required: true + amazon_get_orders: + description: "Get the authenticated user's recent order history. Returns order details including items, delivery address, status, and return eligibility. Requires valid session cookies." + amazon_get_cart: + description: "Get the current Amazon cart contents. Returns items with title, price, quantity, availability, and cart subtotal. Requires valid session cookies." + amazon_add_to_cart: + description: "Add a product to the Amazon cart by ASIN. Navigates to product page and submits the add-to-cart form. Requires valid session cookies." + parameters: + asin: + description: "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')" + required: true + amazon_clear_cart: + description: "Remove all items from the Amazon cart. Iterates through cart items and deletes each one. Requires valid session cookies." diff --git a/integrations/aws/tools.go b/integrations/aws/tools.go index d261bdf3..51049738 100644 --- a/integrations/aws/tools.go +++ b/integrations/aws/tools.go @@ -1,340 +1,12 @@ package aws -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── STS ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_get_caller_identity"), Description: "Get details about the IAM identity making the API call", - Parameters: map[string]string{}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── S3 ─────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_s3_list_buckets"), Description: "List all S3 buckets in the account", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("aws_s3_list_objects"), Description: "List objects in an S3 bucket", - Parameters: map[string]string{"bucket": "Bucket name", "prefix": "Object key prefix filter", "max_keys": "Maximum number of keys to return (default 1000)", "continuation_token": "Token for pagination"}, - Required: []string{"bucket"}, - }, - { - Name: mcp.ToolName("aws_s3_get_object"), Description: "Get an object from S3 (returns metadata and body as text for text types, base64 for binary)", - Parameters: map[string]string{"bucket": "Bucket name", "key": "Object key"}, - Required: []string{"bucket", "key"}, - }, - { - Name: mcp.ToolName("aws_s3_put_object"), Description: "Upload an object to S3", - Parameters: map[string]string{"bucket": "Bucket name", "key": "Object key", "body": "Object content (text)", "content_type": "MIME type (default: application/octet-stream)"}, - Required: []string{"bucket", "key", "body"}, - }, - { - Name: mcp.ToolName("aws_s3_delete_object"), Description: "Delete an object from S3", - Parameters: map[string]string{"bucket": "Bucket name", "key": "Object key"}, - Required: []string{"bucket", "key"}, - }, - { - Name: mcp.ToolName("aws_s3_head_object"), Description: "Get metadata for an S3 object without downloading it", - Parameters: map[string]string{"bucket": "Bucket name", "key": "Object key"}, - Required: []string{"bucket", "key"}, - }, - { - Name: mcp.ToolName("aws_s3_copy_object"), Description: "Copy an object within S3", - Parameters: map[string]string{"source_bucket": "Source bucket name", "source_key": "Source object key", "dest_bucket": "Destination bucket name", "dest_key": "Destination object key"}, - Required: []string{"source_bucket", "source_key", "dest_bucket", "dest_key"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── EC2 ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_ec2_describe_instances"), Description: "List EC2 instances (servers/VMs) with optional filters. Start here for infrastructure inventory and production servers.", - Parameters: map[string]string{"instance_ids": "Comma-separated instance IDs", "filters": "JSON array of filters [{\"Name\":\"tag:Env\",\"Values\":[\"prod\"]}]", "max_results": "Maximum number of results"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_instance"), Description: "Get details for a specific EC2 instance", - Parameters: map[string]string{"instance_id": "Instance ID"}, - Required: []string{"instance_id"}, - }, - { - Name: mcp.ToolName("aws_ec2_start_instances"), Description: "Start one or more EC2 instances", - Parameters: map[string]string{"instance_ids": "Comma-separated instance IDs to start"}, - Required: []string{"instance_ids"}, - }, - { - Name: mcp.ToolName("aws_ec2_stop_instances"), Description: "Stop one or more EC2 instances", - Parameters: map[string]string{"instance_ids": "Comma-separated instance IDs to stop"}, - Required: []string{"instance_ids"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_security_groups"), Description: "List security groups with optional filters", - Parameters: map[string]string{"group_ids": "Comma-separated security group IDs", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_vpcs"), Description: "List VPCs", - Parameters: map[string]string{"vpc_ids": "Comma-separated VPC IDs", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_subnets"), Description: "List subnets", - Parameters: map[string]string{"subnet_ids": "Comma-separated subnet IDs", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_images"), Description: "List AMI images", - Parameters: map[string]string{"image_ids": "Comma-separated AMI IDs", "owners": "Comma-separated owner IDs or aliases (self, amazon)", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_volumes"), Description: "List EBS volumes", - Parameters: map[string]string{"volume_ids": "Comma-separated volume IDs", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_addresses"), Description: "List Elastic IP addresses", - Parameters: map[string]string{"allocation_ids": "Comma-separated allocation IDs", "filters": "JSON array of filters"}, - }, - { - Name: mcp.ToolName("aws_ec2_describe_key_pairs"), Description: "List EC2 key pairs", - Parameters: map[string]string{"key_names": "Comma-separated key pair names"}, - }, - - // ── Lambda ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_lambda_list_functions"), Description: "List Lambda functions", - Parameters: map[string]string{"max_items": "Maximum number of functions to return"}, - }, - { - Name: mcp.ToolName("aws_lambda_get_function"), Description: "Get details about a Lambda function", - Parameters: map[string]string{"function_name": "Function name or ARN"}, - Required: []string{"function_name"}, - }, - { - Name: mcp.ToolName("aws_lambda_invoke"), Description: "Invoke (run/trigger) a Lambda function. Use after list_functions to find the function name.", - Parameters: map[string]string{"function_name": "Function name or ARN", "payload": "JSON payload to pass to the function", "invocation_type": "RequestResponse (sync, default), Event (async), or DryRun"}, - Required: []string{"function_name"}, - }, - { - Name: mcp.ToolName("aws_lambda_list_event_source_mappings"), Description: "List event source mappings for a function", - Parameters: map[string]string{"function_name": "Function name or ARN"}, - }, - { - Name: mcp.ToolName("aws_lambda_get_function_configuration"), Description: "Get the configuration of a Lambda function", - Parameters: map[string]string{"function_name": "Function name or ARN"}, - Required: []string{"function_name"}, - }, - - // ── IAM ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_iam_list_users"), Description: "List IAM users. Start here for access audits and finding who has AWS access.", - Parameters: map[string]string{"path_prefix": "Path prefix for filtering (default /)", "max_items": "Maximum number of users to return"}, - }, - { - Name: mcp.ToolName("aws_iam_get_user"), Description: "Get details about an IAM user", - Parameters: map[string]string{"username": "IAM username (omit for current user)"}, - }, - { - Name: mcp.ToolName("aws_iam_list_roles"), Description: "List IAM roles", - Parameters: map[string]string{"path_prefix": "Path prefix for filtering (default /)", "max_items": "Maximum number of roles to return"}, - }, - { - Name: mcp.ToolName("aws_iam_get_role"), Description: "Get details about an IAM role", - Parameters: map[string]string{"role_name": "IAM role name"}, - Required: []string{"role_name"}, - }, - { - Name: mcp.ToolName("aws_iam_list_policies"), Description: "List IAM policies", - Parameters: map[string]string{"scope": "Scope: All, AWS, Local (default All)", "only_attached": "Only show attached policies (true/false)", "path_prefix": "Path prefix filter", "max_items": "Maximum number of policies to return"}, - }, - { - Name: mcp.ToolName("aws_iam_get_policy"), Description: "Get details about an IAM policy", - Parameters: map[string]string{"policy_arn": "Policy ARN"}, - Required: []string{"policy_arn"}, - }, - { - Name: mcp.ToolName("aws_iam_list_groups"), Description: "List IAM groups", - Parameters: map[string]string{"path_prefix": "Path prefix for filtering", "max_items": "Maximum number of groups to return"}, - }, - { - Name: mcp.ToolName("aws_iam_list_attached_role_policies"), Description: "List policies attached to an IAM role", - Parameters: map[string]string{"role_name": "IAM role name", "path_prefix": "Path prefix filter"}, - Required: []string{"role_name"}, - }, - { - Name: mcp.ToolName("aws_iam_list_attached_user_policies"), Description: "List policies attached to an IAM user", - Parameters: map[string]string{"username": "IAM username", "path_prefix": "Path prefix filter"}, - Required: []string{"username"}, - }, - { - Name: mcp.ToolName("aws_iam_list_attached_group_policies"), Description: "List policies attached to an IAM group", - Parameters: map[string]string{"group_name": "IAM group name", "path_prefix": "Path prefix filter"}, - Required: []string{"group_name"}, - }, - - // ── CloudWatch ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_cloudwatch_list_metrics"), Description: "List CloudWatch metrics", - Parameters: map[string]string{"namespace": "Metric namespace (e.g. AWS/EC2)", "metric_name": "Metric name filter"}, - }, - { - Name: mcp.ToolName("aws_cloudwatch_get_metric_data"), Description: "Get CloudWatch metric data points (time series). Use for monitoring performance over a time range. Use after list_metrics to discover available metrics.", - Parameters: map[string]string{"metric_name": "Metric name", "namespace": "Metric namespace", "stat": "Statistic: Average, Sum, Minimum, Maximum, SampleCount", "period": "Period in seconds (default 300)", "start_time": "Start time (RFC3339 or relative e.g. -1h)", "end_time": "End time (RFC3339 or relative, default now)", "dimensions": "JSON object of dimension key-value pairs"}, - Required: []string{"metric_name", "namespace", "stat"}, - }, - { - Name: mcp.ToolName("aws_cloudwatch_describe_alarms"), Description: "List CloudWatch alarms for active alerts, firing monitors, and threshold warnings. Filter by state (ALARM, OK, INSUFFICIENT_DATA).", - Parameters: map[string]string{"alarm_names": "Comma-separated alarm names", "state_value": "Filter by state: OK, ALARM, INSUFFICIENT_DATA", "max_records": "Maximum number of alarms to return"}, - }, - { - Name: mcp.ToolName("aws_cloudwatch_get_metric_statistics"), Description: "Get statistics for a specific CloudWatch metric", - Parameters: map[string]string{"namespace": "Metric namespace", "metric_name": "Metric name", "start_time": "Start time (RFC3339 or relative e.g. -1h)", "end_time": "End time (RFC3339)", "period": "Period in seconds", "statistics": "Comma-separated: Average, Sum, Minimum, Maximum, SampleCount", "dimensions": "JSON object of dimension key-value pairs"}, - Required: []string{"namespace", "metric_name", "start_time", "period", "statistics"}, - }, - - // ── ECS ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_ecs_list_clusters"), Description: "List ECS clusters", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("aws_ecs_describe_clusters"), Description: "Get details about one or more ECS clusters", - Parameters: map[string]string{"clusters": "Comma-separated cluster names or ARNs"}, - Required: []string{"clusters"}, - }, - { - Name: mcp.ToolName("aws_ecs_list_services"), Description: "List services (deployed containers) in an ECS cluster. Use after list_clusters to find the cluster name.", - Parameters: map[string]string{"cluster": "Cluster name or ARN"}, - Required: []string{"cluster"}, - }, - { - Name: mcp.ToolName("aws_ecs_describe_services"), Description: "Get details about ECS services including deploy status, health, and running/desired count. Use after list_services.", - Parameters: map[string]string{"cluster": "Cluster name or ARN", "services": "Comma-separated service names or ARNs"}, - Required: []string{"cluster", "services"}, - }, - { - Name: mcp.ToolName("aws_ecs_list_tasks"), Description: "List tasks in an ECS cluster", - Parameters: map[string]string{"cluster": "Cluster name or ARN", "service_name": "Filter by service name", "desired_status": "Filter by status: RUNNING, PENDING, STOPPED"}, - }, - { - Name: mcp.ToolName("aws_ecs_describe_tasks"), Description: "Get details about one or more ECS tasks", - Parameters: map[string]string{"cluster": "Cluster name or ARN", "tasks": "Comma-separated task IDs or ARNs"}, - Required: []string{"cluster", "tasks"}, - }, - { - Name: mcp.ToolName("aws_ecs_list_task_definitions"), Description: "List ECS task definition families or revisions", - Parameters: map[string]string{"family_prefix": "Task definition family prefix filter", "status": "Filter: ACTIVE or INACTIVE"}, - }, - { - Name: mcp.ToolName("aws_ecs_describe_task_definition"), Description: "Get details about an ECS task definition", - Parameters: map[string]string{"task_definition": "Task definition family:revision or full ARN"}, - Required: []string{"task_definition"}, - }, - - // ── SNS ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_sns_list_topics"), Description: "List SNS topics", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("aws_sns_get_topic_attributes"), Description: "Get attributes for an SNS topic", - Parameters: map[string]string{"topic_arn": "SNS topic ARN"}, - Required: []string{"topic_arn"}, - }, - { - Name: mcp.ToolName("aws_sns_list_subscriptions"), Description: "List SNS subscriptions", - Parameters: map[string]string{"topic_arn": "Filter by topic ARN"}, - }, - { - Name: mcp.ToolName("aws_sns_publish"), Description: "Publish (send) a notification message to an SNS topic. Use after list_topics to find the topic ARN.", - Parameters: map[string]string{"topic_arn": "SNS topic ARN", "message": "Message body", "subject": "Message subject (for email subscriptions)"}, - Required: []string{"topic_arn", "message"}, - }, - - // ── SQS ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_sqs_list_queues"), Description: "List SQS queues", - Parameters: map[string]string{"queue_name_prefix": "Queue name prefix filter"}, - }, - { - Name: mcp.ToolName("aws_sqs_get_queue_attributes"), Description: "Get attributes for an SQS queue", - Parameters: map[string]string{"queue_url": "SQS queue URL"}, - Required: []string{"queue_url"}, - }, - { - Name: mcp.ToolName("aws_sqs_send_message"), Description: "Send a message to an SQS queue", - Parameters: map[string]string{"queue_url": "SQS queue URL", "message_body": "Message content", "delay_seconds": "Delay in seconds (0-900)"}, - Required: []string{"queue_url", "message_body"}, - }, - { - Name: mcp.ToolName("aws_sqs_receive_message"), Description: "Receive messages from an SQS queue", - Parameters: map[string]string{"queue_url": "SQS queue URL", "max_messages": "Max messages to receive (1-10, default 1)", "wait_time_seconds": "Long poll wait time (0-20)"}, - Required: []string{"queue_url"}, - }, - { - Name: mcp.ToolName("aws_sqs_delete_message"), Description: "Delete a message from an SQS queue", - Parameters: map[string]string{"queue_url": "SQS queue URL", "receipt_handle": "Receipt handle from receive"}, - Required: []string{"queue_url", "receipt_handle"}, - }, - { - Name: mcp.ToolName("aws_sqs_purge_queue"), Description: "Purge all messages from an SQS queue", - Parameters: map[string]string{"queue_url": "SQS queue URL"}, - Required: []string{"queue_url"}, - }, - - // ── DynamoDB ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_dynamodb_list_tables"), Description: "List DynamoDB tables", - Parameters: map[string]string{"limit": "Maximum number of tables to return"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_describe_table"), Description: "Get details about a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name"}, - Required: []string{"table_name"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_get_item"), Description: "Get an item from a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name", "key": "JSON object with key attributes (e.g. {\"id\":{\"S\":\"123\"}})"}, - Required: []string{"table_name", "key"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_put_item"), Description: "Put an item into a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name", "item": "JSON object with item attributes in DynamoDB format"}, - Required: []string{"table_name", "item"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_query"), Description: "Query a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name", "key_condition_expression": "Key condition expression", "expression_attribute_values": "JSON object of expression attribute values in DynamoDB format", "expression_attribute_names": "JSON object of expression attribute name placeholders", "index_name": "Secondary index name", "limit": "Maximum number of items to return", "scan_index_forward": "Sort ascending (true, default) or descending (false)"}, - Required: []string{"table_name", "key_condition_expression", "expression_attribute_values"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_scan"), Description: "Scan a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name", "filter_expression": "Filter expression", "expression_attribute_values": "JSON object of expression attribute values", "expression_attribute_names": "JSON object of expression attribute name placeholders", "limit": "Maximum number of items to return"}, - Required: []string{"table_name"}, - }, - { - Name: mcp.ToolName("aws_dynamodb_delete_item"), Description: "Delete an item from a DynamoDB table", - Parameters: map[string]string{"table_name": "Table name", "key": "JSON object with key attributes in DynamoDB format"}, - Required: []string{"table_name", "key"}, - }, - - // ── CloudFormation ─────────────────────────────────────────────── - { - Name: mcp.ToolName("aws_cloudformation_list_stacks"), Description: "List CloudFormation stacks", - Parameters: map[string]string{"status_filter": "Comma-separated status filter (e.g. CREATE_COMPLETE,UPDATE_COMPLETE)"}, - }, - { - Name: mcp.ToolName("aws_cloudformation_describe_stack"), Description: "Get details about a CloudFormation stack", - Parameters: map[string]string{"stack_name": "Stack name or ID"}, - Required: []string{"stack_name"}, - }, - { - Name: mcp.ToolName("aws_cloudformation_list_stack_resources"), Description: "List resources in a CloudFormation stack", - Parameters: map[string]string{"stack_name": "Stack name or ID"}, - Required: []string{"stack_name"}, - }, - { - Name: mcp.ToolName("aws_cloudformation_get_template"), Description: "Get the template for a CloudFormation stack", - Parameters: map[string]string{"stack_name": "Stack name or ID"}, - Required: []string{"stack_name"}, - }, - { - Name: mcp.ToolName("aws_cloudformation_describe_stack_events"), Description: "List events for a CloudFormation stack. Use to debug deploy failures, rollbacks, or track infrastructure change history.", - Parameters: map[string]string{"stack_name": "Stack name or ID"}, - Required: []string{"stack_name"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/aws/tools.yaml b/integrations/aws/tools.yaml new file mode 100644 index 00000000..9b9deaf7 --- /dev/null +++ b/integrations/aws/tools.yaml @@ -0,0 +1,543 @@ +version: 1 +tools: + aws_get_caller_identity: + description: "Get details about the IAM identity making the API call" + parameters: {} + aws_s3_list_buckets: + description: "List all S3 buckets in the account" + parameters: {} + aws_s3_list_objects: + description: "List objects in an S3 bucket" + parameters: + bucket: + description: "Bucket name" + required: true + prefix: + description: "Object key prefix filter" + max_keys: + description: "Maximum number of keys to return (default 1000)" + continuation_token: + description: "Token for pagination" + aws_s3_get_object: + description: "Get an object from S3 (returns metadata and body as text for text types, base64 for binary)" + parameters: + bucket: + description: "Bucket name" + required: true + key: + description: "Object key" + required: true + aws_s3_put_object: + description: "Upload an object to S3" + parameters: + bucket: + description: "Bucket name" + required: true + key: + description: "Object key" + required: true + body: + description: "Object content (text)" + required: true + content_type: + description: "MIME type (default: application/octet-stream)" + aws_s3_delete_object: + description: "Delete an object from S3" + parameters: + bucket: + description: "Bucket name" + required: true + key: + description: "Object key" + required: true + aws_s3_head_object: + description: "Get metadata for an S3 object without downloading it" + parameters: + bucket: + description: "Bucket name" + required: true + key: + description: "Object key" + required: true + aws_s3_copy_object: + description: "Copy an object within S3" + parameters: + source_bucket: + description: "Source bucket name" + required: true + source_key: + description: "Source object key" + required: true + dest_bucket: + description: "Destination bucket name" + required: true + dest_key: + description: "Destination object key" + required: true + aws_ec2_describe_instances: + description: 'List EC2 instances (servers/VMs) with optional filters. Start here for infrastructure inventory and production servers.' + parameters: + instance_ids: + description: "Comma-separated instance IDs" + filters: + description: 'JSON array of filters [{"Name":"tag:Env","Values":["prod"]}]' + max_results: + description: "Maximum number of results" + aws_ec2_describe_instance: + description: "Get details for a specific EC2 instance" + parameters: + instance_id: + description: "Instance ID" + required: true + aws_ec2_start_instances: + description: "Start one or more EC2 instances" + parameters: + instance_ids: + description: "Comma-separated instance IDs to start" + required: true + aws_ec2_stop_instances: + description: "Stop one or more EC2 instances" + parameters: + instance_ids: + description: "Comma-separated instance IDs to stop" + required: true + aws_ec2_describe_security_groups: + description: "List security groups with optional filters" + parameters: + group_ids: + description: "Comma-separated security group IDs" + filters: + description: "JSON array of filters" + aws_ec2_describe_vpcs: + description: "List VPCs" + parameters: + vpc_ids: + description: "Comma-separated VPC IDs" + filters: + description: "JSON array of filters" + aws_ec2_describe_subnets: + description: "List subnets" + parameters: + subnet_ids: + description: "Comma-separated subnet IDs" + filters: + description: "JSON array of filters" + aws_ec2_describe_images: + description: "List AMI images" + parameters: + image_ids: + description: "Comma-separated AMI IDs" + owners: + description: "Comma-separated owner IDs or aliases (self, amazon)" + filters: + description: "JSON array of filters" + aws_ec2_describe_volumes: + description: "List EBS volumes" + parameters: + volume_ids: + description: "Comma-separated volume IDs" + filters: + description: "JSON array of filters" + aws_ec2_describe_addresses: + description: "List Elastic IP addresses" + parameters: + allocation_ids: + description: "Comma-separated allocation IDs" + filters: + description: "JSON array of filters" + aws_ec2_describe_key_pairs: + description: "List EC2 key pairs" + parameters: + key_names: + description: "Comma-separated key pair names" + aws_lambda_list_functions: + description: "List Lambda functions" + parameters: + max_items: + description: "Maximum number of functions to return" + aws_lambda_get_function: + description: "Get details about a Lambda function" + parameters: + function_name: + description: "Function name or ARN" + required: true + aws_lambda_invoke: + description: "Invoke (run/trigger) a Lambda function. Use after list_functions to find the function name." + parameters: + function_name: + description: "Function name or ARN" + required: true + payload: + description: "JSON payload to pass to the function" + invocation_type: + description: "RequestResponse (sync, default), Event (async), or DryRun" + aws_lambda_list_event_source_mappings: + description: "List event source mappings for a function" + parameters: + function_name: + description: "Function name or ARN" + aws_lambda_get_function_configuration: + description: "Get the configuration of a Lambda function" + parameters: + function_name: + description: "Function name or ARN" + required: true + aws_iam_list_users: + description: "List IAM users. Start here for access audits and finding who has AWS access." + parameters: + path_prefix: + description: "Path prefix for filtering (default /)" + max_items: + description: "Maximum number of users to return" + aws_iam_get_user: + description: "Get details about an IAM user" + parameters: + username: + description: "IAM username (omit for current user)" + aws_iam_list_roles: + description: "List IAM roles" + parameters: + path_prefix: + description: "Path prefix for filtering (default /)" + max_items: + description: "Maximum number of roles to return" + aws_iam_get_role: + description: "Get details about an IAM role" + parameters: + role_name: + description: "IAM role name" + required: true + aws_iam_list_policies: + description: "List IAM policies" + parameters: + scope: + description: "Scope: All, AWS, Local (default All)" + only_attached: + description: "Only show attached policies (true/false)" + path_prefix: + description: "Path prefix filter" + max_items: + description: "Maximum number of policies to return" + aws_iam_get_policy: + description: "Get details about an IAM policy" + parameters: + policy_arn: + description: "Policy ARN" + required: true + aws_iam_list_groups: + description: "List IAM groups" + parameters: + path_prefix: + description: "Path prefix for filtering" + max_items: + description: "Maximum number of groups to return" + aws_iam_list_attached_role_policies: + description: "List policies attached to an IAM role" + parameters: + role_name: + description: "IAM role name" + required: true + path_prefix: + description: "Path prefix filter" + aws_iam_list_attached_user_policies: + description: "List policies attached to an IAM user" + parameters: + username: + description: "IAM username" + required: true + path_prefix: + description: "Path prefix filter" + aws_iam_list_attached_group_policies: + description: "List policies attached to an IAM group" + parameters: + group_name: + description: "IAM group name" + required: true + path_prefix: + description: "Path prefix filter" + aws_cloudwatch_list_metrics: + description: "List CloudWatch metrics" + parameters: + namespace: + description: "Metric namespace (e.g. AWS/EC2)" + metric_name: + description: "Metric name filter" + aws_cloudwatch_get_metric_data: + description: "Get CloudWatch metric data points (time series). Use for monitoring performance over a time range. Use after list_metrics to discover available metrics." + parameters: + metric_name: + description: "Metric name" + required: true + namespace: + description: "Metric namespace" + required: true + stat: + description: "Statistic: Average, Sum, Minimum, Maximum, SampleCount" + required: true + period: + description: "Period in seconds (default 300)" + start_time: + description: "Start time (RFC3339 or relative e.g. -1h)" + end_time: + description: "End time (RFC3339 or relative, default now)" + dimensions: + description: "JSON object of dimension key-value pairs" + aws_cloudwatch_describe_alarms: + description: "List CloudWatch alarms for active alerts, firing monitors, and threshold warnings. Filter by state (ALARM, OK, INSUFFICIENT_DATA)." + parameters: + alarm_names: + description: "Comma-separated alarm names" + state_value: + description: "Filter by state: OK, ALARM, INSUFFICIENT_DATA" + max_records: + description: "Maximum number of alarms to return" + aws_cloudwatch_get_metric_statistics: + description: "Get statistics for a specific CloudWatch metric" + parameters: + namespace: + description: "Metric namespace" + required: true + metric_name: + description: "Metric name" + required: true + start_time: + description: "Start time (RFC3339 or relative e.g. -1h)" + required: true + end_time: + description: "End time (RFC3339)" + period: + description: "Period in seconds" + required: true + statistics: + description: "Comma-separated: Average, Sum, Minimum, Maximum, SampleCount" + required: true + dimensions: + description: "JSON object of dimension key-value pairs" + aws_ecs_list_clusters: + description: "List ECS clusters" + parameters: {} + aws_ecs_describe_clusters: + description: "Get details about one or more ECS clusters" + parameters: + clusters: + description: "Comma-separated cluster names or ARNs" + required: true + aws_ecs_list_services: + description: "List services (deployed containers) in an ECS cluster. Use after list_clusters to find the cluster name." + parameters: + cluster: + description: "Cluster name or ARN" + required: true + aws_ecs_describe_services: + description: "Get details about ECS services including deploy status, health, and running/desired count. Use after list_services." + parameters: + cluster: + description: "Cluster name or ARN" + required: true + services: + description: "Comma-separated service names or ARNs" + required: true + aws_ecs_list_tasks: + description: "List tasks in an ECS cluster" + parameters: + cluster: + description: "Cluster name or ARN" + service_name: + description: "Filter by service name" + desired_status: + description: "Filter by status: RUNNING, PENDING, STOPPED" + aws_ecs_describe_tasks: + description: "Get details about one or more ECS tasks" + parameters: + cluster: + description: "Cluster name or ARN" + required: true + tasks: + description: "Comma-separated task IDs or ARNs" + required: true + aws_ecs_list_task_definitions: + description: "List ECS task definition families or revisions" + parameters: + family_prefix: + description: "Task definition family prefix filter" + status: + description: "Filter: ACTIVE or INACTIVE" + aws_ecs_describe_task_definition: + description: "Get details about an ECS task definition" + parameters: + task_definition: + description: "Task definition family:revision or full ARN" + required: true + aws_sns_list_topics: + description: "List SNS topics" + parameters: {} + aws_sns_get_topic_attributes: + description: "Get attributes for an SNS topic" + parameters: + topic_arn: + description: "SNS topic ARN" + required: true + aws_sns_list_subscriptions: + description: "List SNS subscriptions" + parameters: + topic_arn: + description: "Filter by topic ARN" + aws_sns_publish: + description: "Publish (send) a notification message to an SNS topic. Use after list_topics to find the topic ARN." + parameters: + topic_arn: + description: "SNS topic ARN" + required: true + message: + description: "Message body" + required: true + subject: + description: "Message subject (for email subscriptions)" + aws_sqs_list_queues: + description: "List SQS queues" + parameters: + queue_name_prefix: + description: "Queue name prefix filter" + aws_sqs_get_queue_attributes: + description: "Get attributes for an SQS queue" + parameters: + queue_url: + description: "SQS queue URL" + required: true + aws_sqs_send_message: + description: "Send a message to an SQS queue" + parameters: + queue_url: + description: "SQS queue URL" + required: true + message_body: + description: "Message content" + required: true + delay_seconds: + description: "Delay in seconds (0-900)" + aws_sqs_receive_message: + description: "Receive messages from an SQS queue" + parameters: + queue_url: + description: "SQS queue URL" + required: true + max_messages: + description: "Max messages to receive (1-10, default 1)" + wait_time_seconds: + description: "Long poll wait time (0-20)" + aws_sqs_delete_message: + description: "Delete a message from an SQS queue" + parameters: + queue_url: + description: "SQS queue URL" + required: true + receipt_handle: + description: "Receipt handle from receive" + required: true + aws_sqs_purge_queue: + description: "Purge all messages from an SQS queue" + parameters: + queue_url: + description: "SQS queue URL" + required: true + aws_dynamodb_list_tables: + description: "List DynamoDB tables" + parameters: + limit: + description: "Maximum number of tables to return" + aws_dynamodb_describe_table: + description: "Get details about a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + aws_dynamodb_get_item: + description: "Get an item from a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + key: + description: 'JSON object with key attributes (e.g. {"id":{"S":"123"}})' + required: true + aws_dynamodb_put_item: + description: "Put an item into a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + item: + description: "JSON object with item attributes in DynamoDB format" + required: true + aws_dynamodb_query: + description: "Query a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + key_condition_expression: + description: "Key condition expression" + required: true + expression_attribute_values: + description: "JSON object of expression attribute values in DynamoDB format" + required: true + expression_attribute_names: + description: "JSON object of expression attribute name placeholders" + index_name: + description: "Secondary index name" + limit: + description: "Maximum number of items to return" + scan_index_forward: + description: "Sort ascending (true, default) or descending (false)" + aws_dynamodb_scan: + description: "Scan a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + filter_expression: + description: "Filter expression" + expression_attribute_values: + description: "JSON object of expression attribute values" + expression_attribute_names: + description: "JSON object of expression attribute name placeholders" + limit: + description: "Maximum number of items to return" + aws_dynamodb_delete_item: + description: "Delete an item from a DynamoDB table" + parameters: + table_name: + description: "Table name" + required: true + key: + description: "JSON object with key attributes in DynamoDB format" + required: true + aws_cloudformation_list_stacks: + description: "List CloudFormation stacks" + parameters: + status_filter: + description: "Comma-separated status filter (e.g. CREATE_COMPLETE,UPDATE_COMPLETE)" + aws_cloudformation_describe_stack: + description: "Get details about a CloudFormation stack" + parameters: + stack_name: + description: "Stack name or ID" + required: true + aws_cloudformation_list_stack_resources: + description: "List resources in a CloudFormation stack" + parameters: + stack_name: + description: "Stack name or ID" + required: true + aws_cloudformation_get_template: + description: "Get the template for a CloudFormation stack" + parameters: + stack_name: + description: "Stack name or ID" + required: true + aws_cloudformation_describe_stack_events: + description: "List events for a CloudFormation stack. Use to debug deploy failures, rollbacks, or track infrastructure change history." + parameters: + stack_name: + description: "Stack name or ID" + required: true diff --git a/integrations/botidentity/tools.go b/integrations/botidentity/tools.go index c3e47cd3..9e6858af 100644 --- a/integrations/botidentity/tools.go +++ b/integrations/botidentity/tools.go @@ -1,322 +1,12 @@ package botidentity -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // --- GitHub App Management --- - { - Name: mcp.ToolName("botidentity_gh_get_app"), - Description: "Get the authenticated GitHub App's metadata including name, permissions, and events. Start here for GitHub bot and app identity management.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("botidentity_gh_list_installations"), - Description: "List all installations of a GitHub App across organizations and users. Shows where the bot is installed and its access scope.", - Parameters: map[string]string{ - "per_page": "Results per page (default: 30, max: 100)", - "page": "Page number (default: 1)", - }, - }, - { - Name: mcp.ToolName("botidentity_gh_get_installation"), - Description: "Get details of a specific GitHub App installation including target account, permissions, and repository access. Use after botidentity_gh_list_installations.", - Parameters: map[string]string{ - "installation_id": "Installation ID (from botidentity_gh_list_installations)", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_create_install_token"), - Description: "Create a short-lived installation access token for a GitHub App bot. Token grants the bot's permissions and expires in 1 hour. Use to authenticate bot API calls.", - Parameters: map[string]string{ - "installation_id": "Installation ID to create token for", - "repositories": "JSON array of repository names to scope the token to (optional, defaults to all)", - "permissions": "JSON object of permission overrides e.g. {\"contents\":\"read\",\"issues\":\"write\"} (optional, defaults to app permissions)", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_list_install_repos"), - Description: "List repositories accessible to a GitHub App installation. Shows which repos the bot identity can access.", - Parameters: map[string]string{ - "installation_id": "Installation ID", - "per_page": "Results per page (default: 30, max: 100)", - "page": "Page number (default: 1)", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_add_install_repo"), - Description: "Add a repository to a GitHub App installation. Grants the bot identity access to an additional repository.", - Parameters: map[string]string{ - "installation_id": "Installation ID", - "repository_id": "Repository ID to add (numeric ID, not name)", - }, - Required: []string{"installation_id", "repository_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_remove_install_repo"), - Description: "Remove a repository from a GitHub App installation. Revokes the bot identity's access to a repository.", - Parameters: map[string]string{ - "installation_id": "Installation ID", - "repository_id": "Repository ID to remove (numeric ID, not name)", - }, - Required: []string{"installation_id", "repository_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_suspend_installation"), - Description: "Suspend a GitHub App installation. Disables the bot identity without uninstalling. All API access and webhooks are paused.", - Parameters: map[string]string{ - "installation_id": "Installation ID to suspend", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_unsuspend_installation"), - Description: "Unsuspend a GitHub App installation. Re-enables a previously suspended bot identity, restoring API access and webhooks.", - Parameters: map[string]string{ - "installation_id": "Installation ID to unsuspend", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_delete_installation"), - Description: "Delete a GitHub App installation. Permanently removes the bot identity from an organization or user account. Irreversible.", - Parameters: map[string]string{ - "installation_id": "Installation ID to delete", - }, - Required: []string{"installation_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_get_webhook_config"), - Description: "Get the webhook configuration for a GitHub App. Shows delivery URL, content type, and SSL verification settings.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("botidentity_gh_update_webhook_config"), - Description: "Update the webhook configuration for a GitHub App. Change the delivery URL, content type, secret, or SSL settings.", - Parameters: map[string]string{ - "url": "Webhook payload delivery URL", - "content_type": "Content type: 'json' or 'form' (default: 'form')", - "secret": "Secret for HMAC signature verification", - "insecure_ssl": "SSL verification: '0' (verify) or '1' (skip)", - }, - }, - { - Name: mcp.ToolName("botidentity_gh_list_webhook_deliveries"), - Description: "List recent webhook deliveries for a GitHub App. Shows delivery status, response codes, and timing for debugging bot event handling.", - Parameters: map[string]string{ - "per_page": "Results per page (default: 30, max: 100)", - "cursor": "Pagination cursor from previous response", - }, - }, - { - Name: mcp.ToolName("botidentity_gh_redeliver_webhook"), - Description: "Redeliver a failed webhook for a GitHub App. Retry a specific delivery that the bot failed to process.", - Parameters: map[string]string{ - "delivery_id": "Webhook delivery ID to redeliver", - }, - Required: []string{"delivery_id"}, - }, - { - Name: mcp.ToolName("botidentity_gh_create_app"), - Description: "Create a GitHub App via the manifest flow. Starts a temporary local web server, opens a browser form that auto-submits to GitHub, waits for the user to approve, captures the redirect code, exchanges it for credentials, and stores everything in the bot inventory. All-in-one.", - Parameters: map[string]string{ - "name": "GitHub App name", - "bot_id": "Bot inventory ID to store credentials on (from botidentity_create_bot)", - "url": "App homepage URL (default: https://github.com)", - "webhook_url": "Webhook delivery URL (optional)", - "org": "Organization slug to create the app under (optional, defaults to personal account)", - "permissions": "JSON object of permissions e.g. {\"contents\":\"read\",\"issues\":\"write\"} (optional, sensible defaults provided)", - "events": "JSON array of webhook events e.g. [\"push\",\"pull_request\"] (optional, sensible defaults provided)", - }, - Required: []string{"name"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // --- Slack App Management --- - { - Name: mcp.ToolName("botidentity_slack_create_app"), - Description: "Create a new Slack bot app from a manifest. Defines the bot's name, scopes, event subscriptions, slash commands, and OAuth settings. Start here for Slack bot identity creation.", - Parameters: map[string]string{ - "manifest": "JSON string of the Slack app manifest defining bot name, scopes, features, and settings", - }, - Required: []string{"manifest"}, - }, - { - Name: mcp.ToolName("botidentity_slack_update_app"), - Description: "Update an existing Slack bot app's manifest. Modify the bot's name, scopes, event subscriptions, slash commands, or OAuth settings.", - Parameters: map[string]string{ - "app_id": "Slack app ID to update", - "manifest": "JSON string of the full updated manifest (must include all fields, not just changes)", - }, - Required: []string{"app_id", "manifest"}, - }, - { - Name: mcp.ToolName("botidentity_slack_delete_app"), - Description: "Permanently delete a Slack bot app. Removes the bot identity and revokes all tokens. Irreversible.", - Parameters: map[string]string{ - "app_id": "Slack app ID to delete", - }, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("botidentity_slack_validate_app"), - Description: "Validate a Slack bot app manifest without creating or updating. Check for errors before applying changes to bot configuration.", - Parameters: map[string]string{ - "manifest": "JSON string of the Slack app manifest to validate", - "app_id": "Optional existing app ID to validate against", - }, - Required: []string{"manifest"}, - }, - { - Name: mcp.ToolName("botidentity_slack_export_app"), - Description: "Export the current manifest of an existing Slack bot app. Use to inspect a bot's configuration or as a template for creating new bots.", - Parameters: map[string]string{ - "app_id": "Slack app ID to export manifest from", - }, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("botidentity_slack_get_bot_info"), - Description: "Get information about a Slack bot user including name, icons, and associated app. Use to verify bot identity details.", - Parameters: map[string]string{ - "bot": "Bot user ID (e.g. B12345678)", - "token": "Bot or user token with users:read scope (uses configured slack_config_token if omitted)", - }, - Required: []string{"bot"}, - }, - { - Name: mcp.ToolName("botidentity_slack_rotate_token"), - Description: "Rotate the Slack app configuration token using the refresh token. Returns a new access token and refresh token. The old tokens are invalidated. Config tokens expire after 12 hours.", - Parameters: map[string]string{ - "refresh_token": "The xoxe refresh token (uses configured slack_refresh_token if omitted)", - }, - }, - { - Name: mcp.ToolName("botidentity_slack_set_bot_icon"), - Description: "Set a Slack bot's profile photo from a file path or base64-encoded image. Requires a bot token with users.profile:write scope. Use after botidentity_generate_logo to apply the generated image.", - Parameters: map[string]string{ - "path": "File path to a PNG image (e.g. from botidentity_generate_logo output). Preferred over image_base64.", - "image_base64": "Base64-encoded PNG image data (alternative to path)", - "token": "Bot token with users.profile:write scope (uses configured slack_bot_token if omitted)", - }, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Logo Generation --- - { - Name: mcp.ToolName("botidentity_generate_logo"), - Description: "Open an interactive logo generator in the browser. Preview logos, tweak the prompt, regenerate until satisfied, then confirm. Logo is compressed under 900KB and saved to ~/.config/switchboard/logos/ when bot_id is provided. Start here for bot branding and avatar creation.", - Parameters: map[string]string{ - "prompt": "Initial text prompt for logo generation", - "negative_prompt": "Initial negative prompt (things to avoid)", - "bot_id": "Bot inventory ID — saves logo to config dir and updates inventory automatically", - "model_id": "Bedrock model ID (default: stability.stable-image-core-v1:1). Alternatives: stability.stable-image-ultra-v1:1, stability.sd3-5-large-v1:0", - }, - }, - - // --- Bot Inventory --- - { - Name: mcp.ToolName("botidentity_create_bot"), - Description: "Create a new bot identity across GitHub and Slack. Attempts to create on both platforms, registers in local inventory, and returns next steps for setup (install URLs, tokens to provide). Start here for new bot creation.", - Parameters: map[string]string{ - "id": "Unique bot identifier (e.g. 'deploy-bot', 'ci-notifier')", - "name": "Human-readable display name for the bot", - "slack": "Enable Slack identity: 'true' (default) or 'false'", - "github": "Enable GitHub identity: 'true' (default) or 'false'", - "slack_manifest": "Custom Slack app manifest JSON (optional — a sensible default is generated from the bot name)", - }, - Required: []string{"id", "name"}, - }, - { - Name: mcp.ToolName("botidentity_delete_bot"), - Description: "Delete a bot identity. Removes the Slack app (if created), provides instructions for GitHub App deletion, and removes the local inventory entry.", - Parameters: map[string]string{ - "id": "Bot identifier to delete", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("botidentity_inv_list"), - Description: "List all bots in the local inventory. Shows each bot's enabled platforms, app IDs, and logo. Filter by platform.", - Parameters: map[string]string{ - "platform": "Filter by platform: 'github' or 'slack' (optional, lists all if omitted)", - }, - }, - { - Name: mcp.ToolName("botidentity_inv_get"), - Description: "Get full details of a bot from the inventory including credentials (masked), platform status, and next steps for incomplete setup. Use after botidentity_inv_list.", - Parameters: map[string]string{ - "id": "Bot identifier", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("botidentity_inv_update"), - Description: "Update a bot's profile in the inventory. Change name, logo, or platform-specific IDs.", - Parameters: map[string]string{ - "id": "Bot identifier", - "name": "New display name", - "logo_path": "New logo image path", - "slack_app_id": "Slack App ID (also enables Slack if not already)", - "slack_bot_user_id": "Slack bot user ID", - "github_app_id": "GitHub App ID (also enables GitHub if not already)", - "github_app_slug": "GitHub App slug (for URL generation)", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("botidentity_inv_set_cred"), - Description: "Store a credential for a bot. Well-known keys (slack_bot_token, github_private_key, github_webhook_secret, github_client_id, github_client_secret, slack_webhook_url) are stored in platform-specific fields. Other keys go to the generic credentials map.", - Parameters: map[string]string{ - "id": "Bot identifier", - "key": "Credential key (e.g. 'slack_bot_token', 'github_private_key', 'github_webhook_secret')", - "value": "Credential value", - }, - Required: []string{"id", "key", "value"}, - }, - { - Name: mcp.ToolName("botidentity_inv_delete_cred"), - Description: "Remove a stored credential from a bot in the inventory.", - Parameters: map[string]string{ - "id": "Bot identifier", - "key": "Credential key to remove", - }, - Required: []string{"id", "key"}, - }, - { - Name: mcp.ToolName("botidentity_inv_get_creds"), - Description: "List all credentials stored for a bot (values masked). Use botidentity_inv_get_cred to reveal a specific value.", - Parameters: map[string]string{ - "id": "Bot identifier", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("botidentity_inv_get_cred"), - Description: "Get the full unmasked value of a specific credential for a bot. Use after botidentity_inv_get_creds to identify the key.", - Parameters: map[string]string{ - "id": "Bot identifier", - "key": "Credential key to retrieve", - }, - Required: []string{"id", "key"}, - }, - { - Name: mcp.ToolName("botidentity_inv_set_logo"), - Description: "Set or update a bot's logo path in the inventory. Use after botidentity_generate_logo to associate the generated image.", - Parameters: map[string]string{ - "id": "Bot identifier", - "logo_path": "Path to logo image file", - }, - Required: []string{"id", "logo_path"}, - }, - { - Name: mcp.ToolName("botidentity_inv_set_meta"), - Description: "Set a metadata key-value pair on a bot in the inventory. Use for descriptions, environment, team ownership, or any custom labels.", - Parameters: map[string]string{ - "id": "Bot identifier", - "key": "Metadata key (e.g. 'environment', 'team', 'description')", - "value": "Metadata value", - }, - Required: []string{"id", "key", "value"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/botidentity/tools.yaml b/integrations/botidentity/tools.yaml new file mode 100644 index 00000000..2f7993d0 --- /dev/null +++ b/integrations/botidentity/tools.yaml @@ -0,0 +1,294 @@ +version: 1 +tools: + botidentity_gh_get_app: + description: "Get the authenticated GitHub App's metadata including name, permissions, and events. Start here for GitHub bot and app identity management." + parameters: {} + botidentity_gh_list_installations: + description: "List all installations of a GitHub App across organizations and users. Shows where the bot is installed and its access scope." + parameters: + per_page: + description: "Results per page (default: 30, max: 100)" + page: + description: "Page number (default: 1)" + botidentity_gh_get_installation: + description: "Get details of a specific GitHub App installation including target account, permissions, and repository access. Use after botidentity_gh_list_installations." + parameters: + installation_id: + description: "Installation ID (from botidentity_gh_list_installations)" + required: true + botidentity_gh_create_install_token: + description: "Create a short-lived installation access token for a GitHub App bot. Token grants the bot's permissions and expires in 1 hour. Use to authenticate bot API calls." + parameters: + installation_id: + description: "Installation ID to create token for" + required: true + repositories: + description: "JSON array of repository names to scope the token to (optional, defaults to all)" + permissions: + description: 'JSON object of permission overrides e.g. {"contents":"read","issues":"write"} (optional, defaults to app permissions)' + botidentity_gh_list_install_repos: + description: "List repositories accessible to a GitHub App installation. Shows which repos the bot identity can access." + parameters: + installation_id: + description: "Installation ID" + required: true + per_page: + description: "Results per page (default: 30, max: 100)" + page: + description: "Page number (default: 1)" + botidentity_gh_add_install_repo: + description: "Add a repository to a GitHub App installation. Grants the bot identity access to an additional repository." + parameters: + installation_id: + description: "Installation ID" + required: true + repository_id: + description: "Repository ID to add (numeric ID, not name)" + required: true + botidentity_gh_remove_install_repo: + description: "Remove a repository from a GitHub App installation. Revokes the bot identity's access to a repository." + parameters: + installation_id: + description: "Installation ID" + required: true + repository_id: + description: "Repository ID to remove (numeric ID, not name)" + required: true + botidentity_gh_suspend_installation: + description: "Suspend a GitHub App installation. Disables the bot identity without uninstalling. All API access and webhooks are paused." + parameters: + installation_id: + description: "Installation ID to suspend" + required: true + botidentity_gh_unsuspend_installation: + description: "Unsuspend a GitHub App installation. Re-enables a previously suspended bot identity, restoring API access and webhooks." + parameters: + installation_id: + description: "Installation ID to unsuspend" + required: true + botidentity_gh_delete_installation: + description: "Delete a GitHub App installation. Permanently removes the bot identity from an organization or user account. Irreversible." + parameters: + installation_id: + description: "Installation ID to delete" + required: true + botidentity_gh_get_webhook_config: + description: "Get the webhook configuration for a GitHub App. Shows delivery URL, content type, and SSL verification settings." + parameters: {} + botidentity_gh_update_webhook_config: + description: "Update the webhook configuration for a GitHub App. Change the delivery URL, content type, secret, or SSL settings." + parameters: + url: + description: "Webhook payload delivery URL" + content_type: + description: "Content type: 'json' or 'form' (default: 'form')" + secret: + description: "Secret for HMAC signature verification" + insecure_ssl: + description: "SSL verification: '0' (verify) or '1' (skip)" + botidentity_gh_list_webhook_deliveries: + description: "List recent webhook deliveries for a GitHub App. Shows delivery status, response codes, and timing for debugging bot event handling." + parameters: + per_page: + description: "Results per page (default: 30, max: 100)" + cursor: + description: "Pagination cursor from previous response" + botidentity_gh_redeliver_webhook: + description: "Redeliver a failed webhook for a GitHub App. Retry a specific delivery that the bot failed to process." + parameters: + delivery_id: + description: "Webhook delivery ID to redeliver" + required: true + botidentity_gh_create_app: + description: "Create a GitHub App via the manifest flow. Starts a temporary local web server, opens a browser form that auto-submits to GitHub, waits for the user to approve, captures the redirect code, exchanges it for credentials, and stores everything in the bot inventory. All-in-one." + parameters: + name: + description: "GitHub App name" + required: true + bot_id: + description: "Bot inventory ID to store credentials on (from botidentity_create_bot)" + url: + description: "App homepage URL (default: https://github.com)" + webhook_url: + description: "Webhook delivery URL (optional)" + org: + description: "Organization slug to create the app under (optional, defaults to personal account)" + permissions: + description: 'JSON object of permissions e.g. {"contents":"read","issues":"write"} (optional, sensible defaults provided)' + events: + description: 'JSON array of webhook events e.g. ["push","pull_request"] (optional, sensible defaults provided)' + botidentity_slack_create_app: + description: "Create a new Slack bot app from a manifest. Defines the bot's name, scopes, event subscriptions, slash commands, and OAuth settings. Start here for Slack bot identity creation." + parameters: + manifest: + description: "JSON string of the Slack app manifest defining bot name, scopes, features, and settings" + required: true + botidentity_slack_update_app: + description: "Update an existing Slack bot app's manifest. Modify the bot's name, scopes, event subscriptions, slash commands, or OAuth settings." + parameters: + app_id: + description: "Slack app ID to update" + required: true + manifest: + description: "JSON string of the full updated manifest (must include all fields, not just changes)" + required: true + botidentity_slack_delete_app: + description: "Permanently delete a Slack bot app. Removes the bot identity and revokes all tokens. Irreversible." + parameters: + app_id: + description: "Slack app ID to delete" + required: true + botidentity_slack_validate_app: + description: "Validate a Slack bot app manifest without creating or updating. Check for errors before applying changes to bot configuration." + parameters: + manifest: + description: "JSON string of the Slack app manifest to validate" + required: true + app_id: + description: "Optional existing app ID to validate against" + botidentity_slack_export_app: + description: "Export the current manifest of an existing Slack bot app. Use to inspect a bot's configuration or as a template for creating new bots." + parameters: + app_id: + description: "Slack app ID to export manifest from" + required: true + botidentity_slack_get_bot_info: + description: "Get information about a Slack bot user including name, icons, and associated app. Use to verify bot identity details." + parameters: + bot: + description: "Bot user ID (e.g. B12345678)" + required: true + token: + description: "Bot or user token with users:read scope (uses configured slack_config_token if omitted)" + botidentity_slack_rotate_token: + description: "Rotate the Slack app configuration token using the refresh token. Returns a new access token and refresh token. The old tokens are invalidated. Config tokens expire after 12 hours." + parameters: + refresh_token: + description: "The xoxe refresh token (uses configured slack_refresh_token if omitted)" + botidentity_slack_set_bot_icon: + description: "Set a Slack bot's profile photo from a file path or base64-encoded image. Requires a bot token with users.profile:write scope. Use after botidentity_generate_logo to apply the generated image." + parameters: + path: + description: "File path to a PNG image (e.g. from botidentity_generate_logo output). Preferred over image_base64." + image_base64: + description: "Base64-encoded PNG image data (alternative to path)" + token: + description: "Bot token with users.profile:write scope (uses configured slack_bot_token if omitted)" + botidentity_generate_logo: + description: "Open an interactive logo generator in the browser. Preview logos, tweak the prompt, regenerate until satisfied, then confirm. Logo is compressed under 900KB and saved to ~/.config/switchboard/logos/ when bot_id is provided. Start here for bot branding and avatar creation." + parameters: + prompt: + description: "Initial text prompt for logo generation" + negative_prompt: + description: "Initial negative prompt (things to avoid)" + bot_id: + description: "Bot inventory ID — saves logo to config dir and updates inventory automatically" + model_id: + description: "Bedrock model ID (default: stability.stable-image-core-v1:1). Alternatives: stability.stable-image-ultra-v1:1, stability.sd3-5-large-v1:0" + botidentity_create_bot: + description: "Create a new bot identity across GitHub and Slack. Attempts to create on both platforms, registers in local inventory, and returns next steps for setup (install URLs, tokens to provide). Start here for new bot creation." + parameters: + id: + description: "Unique bot identifier (e.g. 'deploy-bot', 'ci-notifier')" + required: true + name: + description: "Human-readable display name for the bot" + required: true + slack: + description: "Enable Slack identity: 'true' (default) or 'false'" + github: + description: "Enable GitHub identity: 'true' (default) or 'false'" + slack_manifest: + description: "Custom Slack app manifest JSON (optional — a sensible default is generated from the bot name)" + botidentity_delete_bot: + description: "Delete a bot identity. Removes the Slack app (if created), provides instructions for GitHub App deletion, and removes the local inventory entry." + parameters: + id: + description: "Bot identifier to delete" + required: true + botidentity_inv_list: + description: "List all bots in the local inventory. Shows each bot's enabled platforms, app IDs, and logo. Filter by platform." + parameters: + platform: + description: "Filter by platform: 'github' or 'slack' (optional, lists all if omitted)" + botidentity_inv_get: + description: "Get full details of a bot from the inventory including credentials (masked), platform status, and next steps for incomplete setup. Use after botidentity_inv_list." + parameters: + id: + description: "Bot identifier" + required: true + botidentity_inv_update: + description: "Update a bot's profile in the inventory. Change name, logo, or platform-specific IDs." + parameters: + id: + description: "Bot identifier" + required: true + name: + description: "New display name" + logo_path: + description: "New logo image path" + slack_app_id: + description: "Slack App ID (also enables Slack if not already)" + slack_bot_user_id: + description: "Slack bot user ID" + github_app_id: + description: "GitHub App ID (also enables GitHub if not already)" + github_app_slug: + description: "GitHub App slug (for URL generation)" + botidentity_inv_set_cred: + description: "Store a credential for a bot. Well-known keys (slack_bot_token, github_private_key, github_webhook_secret, github_client_id, github_client_secret, slack_webhook_url) are stored in platform-specific fields. Other keys go to the generic credentials map." + parameters: + id: + description: "Bot identifier" + required: true + key: + description: "Credential key (e.g. 'slack_bot_token', 'github_private_key', 'github_webhook_secret')" + required: true + value: + description: "Credential value" + required: true + botidentity_inv_delete_cred: + description: "Remove a stored credential from a bot in the inventory." + parameters: + id: + description: "Bot identifier" + required: true + key: + description: "Credential key to remove" + required: true + botidentity_inv_get_creds: + description: "List all credentials stored for a bot (values masked). Use botidentity_inv_get_cred to reveal a specific value." + parameters: + id: + description: "Bot identifier" + required: true + botidentity_inv_get_cred: + description: "Get the full unmasked value of a specific credential for a bot. Use after botidentity_inv_get_creds to identify the key." + parameters: + id: + description: "Bot identifier" + required: true + key: + description: "Credential key to retrieve" + required: true + botidentity_inv_set_logo: + description: "Set or update a bot's logo path in the inventory. Use after botidentity_generate_logo to associate the generated image." + parameters: + id: + description: "Bot identifier" + required: true + logo_path: + description: "Path to logo image file" + required: true + botidentity_inv_set_meta: + description: "Set a metadata key-value pair on a bot in the inventory. Use for descriptions, environment, team ownership, or any custom labels." + parameters: + id: + description: "Bot identifier" + required: true + key: + description: "Metadata key (e.g. 'environment', 'team', 'description')" + required: true + value: + description: "Metadata value" + required: true diff --git a/integrations/clickhouse/tools.go b/integrations/clickhouse/tools.go index 803b584c..f564b76b 100644 --- a/integrations/clickhouse/tools.go +++ b/integrations/clickhouse/tools.go @@ -1,181 +1,15 @@ package clickhouse -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -const connectionParamDesc = "Connection alias (omit to use default). Use clickhouse_list_connections to see configured clusters." + mcp "github.com/daltoniam/switchboard" +) -var tools = []mcp.ToolDefinition{ - { - Name: mcp.ToolName("clickhouse_list_connections"), - Description: "List all configured ClickHouse cluster connections with their alias, host, database, TLS, and default status. Start here when choosing which cluster to query.", - Parameters: map[string]string{}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Queries --- - { - Name: mcp.ToolName("clickhouse_execute_query"), - Description: "Execute a SQL query (SELECT, SHOW, DESCRIBE, or DDL) against a ClickHouse analytics database and return results as JSON rows", - Parameters: map[string]string{ - "query": "SQL query string to execute", - "database": "Optional ClickHouse database context to run the query in (overrides configured default)", - "connection": connectionParamDesc, - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("clickhouse_explain_query"), - Description: "Run EXPLAIN on a query to show the execution plan", - Parameters: map[string]string{ - "query": "SQL query to explain", - "type": "EXPLAIN type: PLAN (default), PIPELINE, SYNTAX, AST, ESTIMATE", - "connection": connectionParamDesc, - }, - Required: []string{"query"}, - }, - - // --- Databases --- - { - Name: mcp.ToolName("clickhouse_list_databases"), - Description: "List all databases in a ClickHouse cluster. Start here for schema discovery after choosing a connection.", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_tables"), - Description: "List all tables in a ClickHouse database with engine, row count, and size", - Parameters: map[string]string{ - "database": "ClickHouse database name (defaults to current database)", - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_describe_table"), - Description: "Describe a table's columns with names, types, default expressions, and comments", - Parameters: map[string]string{ - "database": "ClickHouse database name (defaults to current database)", - "table": "Table name", - "connection": connectionParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("clickhouse_list_columns"), - Description: "List detailed column metadata for a table from system.columns", - Parameters: map[string]string{ - "database": "ClickHouse database name (defaults to current database)", - "table": "Table name", - "connection": connectionParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("clickhouse_show_create_table"), - Description: "Show the CREATE TABLE statement for a table", - Parameters: map[string]string{ - "database": "ClickHouse database name (defaults to current database)", - "table": "Table name", - "connection": connectionParamDesc, - }, - Required: []string{"table"}, - }, - - // --- System Info --- - { - Name: mcp.ToolName("clickhouse_server_info"), - Description: "Get ClickHouse server version, uptime, and OS info", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_processes"), - Description: "List currently running queries/processes", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_kill_query"), - Description: "Kill a running query by its query ID", - Parameters: map[string]string{ - "query_id": "The query_id of the query to kill", - "connection": connectionParamDesc, - }, - Required: []string{"query_id"}, - }, - { - Name: mcp.ToolName("clickhouse_list_settings"), - Description: "List ClickHouse server settings. Optionally filter by name pattern", - Parameters: map[string]string{ - "pattern": "Optional LIKE pattern to filter setting names (e.g. '%memory%')", - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_merges"), - Description: "List currently running background merges for MergeTree tables", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_replicas"), - Description: "List replica status for replicated tables", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_disk_usage"), - Description: "Show disk usage per ClickHouse database (total bytes, row counts, part counts)", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_parts"), - Description: "List table parts with sizes for a given table (useful for debugging MergeTree)", - Parameters: map[string]string{ - "database": "ClickHouse database name (defaults to current database)", - "table": "Table name", - "active": "Only show active parts (default: true)", - "connection": connectionParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("clickhouse_list_dictionaries"), - Description: "List external dictionaries loaded in the server", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_users"), - Description: "List all users configured in ClickHouse", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_list_roles"), - Description: "List all roles configured in ClickHouse", - Parameters: map[string]string{ - "connection": connectionParamDesc, - }, - }, - { - Name: mcp.ToolName("clickhouse_query_log"), - Description: "Search recent entries from system.query_log", - Parameters: map[string]string{ - "query_pattern": "Optional LIKE pattern to filter by query text", - "query_type": "Filter by type: QueryStart, QueryFinish, ExceptionBeforeStart, ExceptionWhileProcessing", - "limit": "Max rows to return (default: 50)", - "connection": connectionParamDesc, - }, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ mcp.ToolName("clickhouse_list_connections"): listConnections, diff --git a/integrations/clickhouse/tools.yaml b/integrations/clickhouse/tools.yaml new file mode 100644 index 00000000..3a4480b8 --- /dev/null +++ b/integrations/clickhouse/tools.yaml @@ -0,0 +1,107 @@ +version: 1 +tools: + clickhouse_list_connections: + description: "List all configured ClickHouse cluster connections with their alias, host, database, TLS, and default status. Start here when choosing which cluster to query." + parameters: {} + clickhouse_execute_query: + description: "Execute a SQL query (SELECT, SHOW, DESCRIBE, or DDL) against a ClickHouse analytics database and return results as JSON rows" + parameters: + query: + description: "SQL query string to execute" + required: true + database: + description: "Optional database context to run the query in (overrides configured default)" + clickhouse_explain_query: + description: "Run EXPLAIN on a query to show the execution plan" + parameters: + query: + description: "SQL query to explain" + required: true + type: + description: "EXPLAIN type: PLAN (default), PIPELINE, SYNTAX, AST, ESTIMATE" + clickhouse_list_databases: + description: "List all databases in the ClickHouse server. Start here for schema discovery." + parameters: {} + clickhouse_list_tables: + description: "List all tables in a database with engine, row count, and size" + parameters: + database: + description: "Database name (defaults to current database)" + clickhouse_describe_table: + description: "Describe a table's columns with names, types, default expressions, and comments" + parameters: + database: + description: "Database name (defaults to current database)" + table: + description: "Table name" + required: true + clickhouse_list_columns: + description: "List detailed column metadata for a table from system.columns" + parameters: + database: + description: "Database name (defaults to current database)" + table: + description: "Table name" + required: true + clickhouse_show_create_table: + description: "Show the CREATE TABLE statement for a table" + parameters: + database: + description: "Database name (defaults to current database)" + table: + description: "Table name" + required: true + clickhouse_server_info: + description: "Get ClickHouse server version, uptime, and OS info" + parameters: {} + clickhouse_list_processes: + description: "List currently running queries/processes" + parameters: {} + clickhouse_kill_query: + description: "Kill a running query by its query ID" + parameters: + query_id: + description: "The query_id of the query to kill" + required: true + clickhouse_list_settings: + description: "List ClickHouse server settings. Optionally filter by name pattern" + parameters: + pattern: + description: "Optional LIKE pattern to filter setting names (e.g. '%memory%')" + clickhouse_list_merges: + description: "List currently running background merges for MergeTree tables" + parameters: {} + clickhouse_list_replicas: + description: "List replica status for replicated tables" + parameters: {} + clickhouse_disk_usage: + description: "Show disk usage per database (total bytes, row counts, part counts)" + parameters: {} + clickhouse_list_parts: + description: "List table parts with sizes for a given table (useful for debugging MergeTree)" + parameters: + database: + description: "Database name (defaults to current database)" + table: + description: "Table name" + required: true + active: + description: "Only show active parts (default: true)" + clickhouse_list_dictionaries: + description: "List external dictionaries loaded in the server" + parameters: {} + clickhouse_list_users: + description: "List all users configured in ClickHouse" + parameters: {} + clickhouse_list_roles: + description: "List all roles configured in ClickHouse" + parameters: {} + clickhouse_query_log: + description: "Search recent entries from system.query_log" + parameters: + query_pattern: + description: "Optional LIKE pattern to filter by query text" + query_type: + description: "Filter by type: QueryStart, QueryFinish, ExceptionBeforeStart, ExceptionWhileProcessing" + limit: + description: "Max rows to return (default: 50)" diff --git a/integrations/cloudflare/tools.go b/integrations/cloudflare/tools.go index 1c16d7bb..8235d7b6 100644 --- a/integrations/cloudflare/tools.go +++ b/integrations/cloudflare/tools.go @@ -1,875 +1,12 @@ package cloudflare -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Zones ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_zones"), - Description: "List Cloudflare zones (domains). Start here for DNS management, CDN configuration, and website performance. Zones represent domains registered with Cloudflare.", - Parameters: map[string]string{ - "name": "Zone name filter (exact domain match, e.g. example.com)", - "status": "Zone status filter (active, pending, initializing, moved, deleted, deactivated, read_only)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20, max 50)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_zone"), - Description: "Get details for a specific Cloudflare zone including status, nameservers, and settings. Use after list_zones.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_zone"), - Description: "Add a new domain (zone) to Cloudflare. Requires account_id.", - Parameters: map[string]string{ - "name": "Domain name (e.g. example.com)", - "account_id": "Account identifier (defaults to configured account_id)", - "type": "Zone type: full (default) or partial", - "jump_start": "Auto-fetch DNS records (true/false, default false)", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("cloudflare_edit_zone"), - Description: "Update zone settings like paused status or vanity nameservers. Use after get_zone.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "paused": "Pause Cloudflare for this zone (true/false)", - "plan": "Plan identifier to change zone plan", - }, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_zone"), - Description: "Remove a zone (domain) from Cloudflare. Irreversible.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_purge_cache"), - Description: "Purge cached content for a Cloudflare zone. Purge everything or specific URLs/tags/hosts. Use for cache invalidation after deployments.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "purge_everything": "Purge all cached content (true/false)", - "files": "JSON array of URLs to purge (alternative to purge_everything)", - "tags": "Comma-separated cache tags to purge", - "hosts": "Comma-separated hostnames to purge", - }, - Required: []string{"zone_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── DNS Records ────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_dns_records"), - Description: "List DNS records for a Cloudflare zone. View A, AAAA, CNAME, MX, TXT, NS, and other record types. Start here for DNS management and troubleshooting.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "type": "DNS record type filter (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)", - "name": "DNS record name filter (e.g. subdomain.example.com)", - "content": "DNS record content filter (e.g. IP address)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20, max 100)", - }, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_dns_record"), - Description: "Get details for a specific DNS record. Use after list_dns_records.", - Parameters: map[string]string{"zone_id": "Zone identifier", "record_id": "DNS record identifier"}, - Required: []string{"zone_id", "record_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_dns_record"), - Description: "Create a new DNS record in a Cloudflare zone. Add A, AAAA, CNAME, MX, TXT records and more.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "type": "DNS record type (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)", - "name": "DNS record name (e.g. subdomain or @ for root)", - "content": "DNS record content (e.g. IP address, target domain)", - "ttl": "TTL in seconds (1 = automatic, default 1)", - "proxied": "Whether traffic is proxied through Cloudflare (true/false)", - "priority": "MX/SRV record priority", - }, - Required: []string{"zone_id", "type", "name", "content"}, - }, - { - Name: mcp.ToolName("cloudflare_update_dns_record"), - Description: "Update an existing DNS record. Use after list_dns_records to get record_id.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "record_id": "DNS record identifier", - "type": "DNS record type", - "name": "DNS record name", - "content": "DNS record content", - "ttl": "TTL in seconds (1 = automatic)", - "proxied": "Whether traffic is proxied through Cloudflare (true/false)", - "priority": "MX/SRV record priority", - }, - Required: []string{"zone_id", "record_id", "type", "name", "content"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_dns_record"), - Description: "Delete a DNS record from a Cloudflare zone.", - Parameters: map[string]string{"zone_id": "Zone identifier", "record_id": "DNS record identifier"}, - Required: []string{"zone_id", "record_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Workers ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_workers"), - Description: "List Cloudflare Workers scripts. View serverless functions, edge computing workers, and deployed scripts. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_worker"), - Description: "Get metadata for a specific Cloudflare Worker script including bindings, routes, and compatibility settings. Use after list_workers.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "script_name": "Worker script name"}, - Required: []string{"script_name"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_worker"), - Description: "Delete a Cloudflare Worker script.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "script_name": "Worker script name"}, - Required: []string{"script_name"}, - }, - { - Name: mcp.ToolName("cloudflare_list_worker_routes"), - Description: "List Worker routes for a zone. Shows URL patterns mapped to Worker scripts.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - - // ── Pages ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_pages_projects"), - Description: "List Cloudflare Pages projects. View static sites, Jamstack deployments, and frontend projects hosted on Pages. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_pages_project"), - Description: "Get details for a Cloudflare Pages project including build config, domains, and deployment settings. Use after list_pages_projects.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name"}, - Required: []string{"project_name"}, - }, - { - Name: mcp.ToolName("cloudflare_list_pages_deployments"), - Description: "List deployments for a Cloudflare Pages project. View deploy history, build status, and rollback targets.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name"}, - Required: []string{"project_name"}, - }, - { - Name: mcp.ToolName("cloudflare_get_pages_deployment"), - Description: "Get details for a specific Pages deployment including build logs and environment. Use after list_pages_deployments.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name", "deployment_id": "Deployment identifier"}, - Required: []string{"project_name", "deployment_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_pages_deployment"), - Description: "Delete a specific Pages deployment.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name", "deployment_id": "Deployment identifier"}, - Required: []string{"project_name", "deployment_id"}, - }, - { - Name: mcp.ToolName("cloudflare_rollback_pages_deployment"), - Description: "Rollback a Pages project to a previous deployment. Use after list_pages_deployments to find deployment_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name", "deployment_id": "Deployment identifier to rollback to"}, - Required: []string{"project_name", "deployment_id"}, - }, - - // ── R2 ─────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_r2_buckets"), - Description: "List R2 object storage buckets. Cloudflare R2 is S3-compatible storage with zero egress fees. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_create_r2_bucket"), - Description: "Create a new R2 object storage bucket.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "name": "Bucket name"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_r2_bucket"), - Description: "Delete an R2 object storage bucket. Bucket must be empty.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "name": "Bucket name"}, - Required: []string{"name"}, - }, - - // ── KV ─────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_kv_namespaces"), - Description: "List Workers KV namespaces. KV is Cloudflare's global key-value store for edge data. Requires account_id.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_create_kv_namespace"), - Description: "Create a new Workers KV namespace.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "title": "Namespace title"}, - Required: []string{"title"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_kv_namespace"), - Description: "Delete a Workers KV namespace and all its keys.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "namespace_id": "KV namespace identifier"}, - Required: []string{"namespace_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_kv_keys"), - Description: "List keys in a Workers KV namespace. Returns key names with optional metadata.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "namespace_id": "KV namespace identifier", - "prefix": "Key prefix filter", - "limit": "Maximum keys to return (default 1000)", - "cursor": "Pagination cursor from previous response", - }, - Required: []string{"namespace_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_kv_value"), - Description: "Read a value from Workers KV by key. Returns the stored value as text.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "namespace_id": "KV namespace identifier", - "key_name": "Key name to read", - }, - Required: []string{"namespace_id", "key_name"}, - }, - { - Name: mcp.ToolName("cloudflare_put_kv_value"), - Description: "Write a key-value pair to Workers KV.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "namespace_id": "KV namespace identifier", - "key_name": "Key name", - "value": "Value to store", - "metadata": "JSON metadata object to attach to the key", - }, - Required: []string{"namespace_id", "key_name", "value"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_kv_value"), - Description: "Delete a key-value pair from Workers KV.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "namespace_id": "KV namespace identifier", - "key_name": "Key name to delete", - }, - Required: []string{"namespace_id", "key_name"}, - }, - - // ── D1 ─────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_d1_databases"), - Description: "List D1 SQL databases. D1 is Cloudflare's serverless SQLite database for Workers. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_d1_database"), - Description: "Get details for a specific D1 database including size and table count. Use after list_d1_databases.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "database_id": "D1 database identifier"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_d1_database"), - Description: "Create a new D1 SQL database.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "name": "Database name"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_d1_database"), - Description: "Delete a D1 SQL database and all its data. Irreversible.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "database_id": "D1 database identifier"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("cloudflare_query_d1_database"), - Description: "Execute a SQL query against a D1 database. Supports SELECT, INSERT, UPDATE, DELETE and DDL. Use parameterized queries with params array for safety.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "database_id": "D1 database identifier", - "sql": "SQL query to execute", - "params": "JSON array of query parameters for prepared statements", - }, - Required: []string{"database_id", "sql"}, - }, - - // ── Firewall / WAF ────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_waf_rulesets"), - Description: "List WAF (Web Application Firewall) rulesets for a zone. View security rules protecting against attacks, bots, and threats.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_waf_ruleset"), - Description: "Get details for a specific WAF ruleset including individual rules and their actions. Use after list_waf_rulesets.", - Parameters: map[string]string{"zone_id": "Zone identifier", "ruleset_id": "Ruleset identifier"}, - Required: []string{"zone_id", "ruleset_id"}, - }, - - // ── Load Balancers ────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_load_balancers"), - Description: "List load balancers for a zone. View traffic distribution, health checks, and failover configuration.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_load_balancer"), - Description: "Get details for a specific load balancer including pools, rules, and session affinity. Use after list_load_balancers.", - Parameters: map[string]string{"zone_id": "Zone identifier", "lb_id": "Load balancer identifier"}, - Required: []string{"zone_id", "lb_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_lb_pools"), - Description: "List load balancer pools (origin server groups). View pool health, origins, and traffic steering. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_lb_pool"), - Description: "Get details for a specific load balancer pool including origins and health check results. Use after list_lb_pools.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "pool_id": "Pool identifier"}, - Required: []string{"pool_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_lb_monitors"), - Description: "List load balancer health monitors. View health check configurations for origin pools. Requires account_id.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - - // ── Analytics ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_get_zone_analytics"), - Description: "Get zone analytics dashboard data including requests, bandwidth, threats, and page views. Provides traffic overview and performance metrics for a Cloudflare zone.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "since": "Start time (ISO 8601 or relative like -1440 for minutes ago, default -1440)", - "until": "End time (ISO 8601 or relative, default 0 for now)", - }, - Required: []string{"zone_id"}, - }, - - // ── Accounts ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_accounts"), - Description: "List Cloudflare accounts the API token has access to.", - Parameters: map[string]string{ - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_account"), - Description: "Get details for a specific Cloudflare account. Use after list_accounts.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_list_account_members"), - Description: "List members of a Cloudflare account with their roles and permissions.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - - // ── AI Gateway ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_ai_gateways"), - Description: "List Cloudflare AI Gateways for an account. AI Gateway proxies LLM traffic to OpenAI, Anthropic, Workers AI, and other providers with caching, rate limiting, logging, and cost tracking. Start here to discover gateway IDs before pulling logs, token usage, or spend.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_ai_gateway"), - Description: "Get a Cloudflare AI Gateway's configuration: cache TTL, rate limits, log collection, spend limits, authentication, DLP, and guardrails. Use after list_ai_gateways.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "gateway_id": "AI Gateway identifier", - }, - Required: []string{"gateway_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_ai_gateway_logs"), - Description: "List AI Gateway request logs with per-request token usage (tokens_in, tokens_out), cost, latency, model, provider, cache hits, and success/error status. Use this to audit LLM spend, debug failing inferences, or attribute token consumption to a model or provider. Filter by date, provider, model, success, cached, or feedback.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "gateway_id": "AI Gateway identifier", - "start_date": "Start of time range (ISO 8601, e.g. 2024-01-01T00:00:00Z)", - "end_date": "End of time range (ISO 8601)", - "provider": "Filter by LLM provider (e.g. openai, anthropic, workers-ai)", - "model": "Filter by model id (e.g. gpt-4o, claude-3-5-sonnet)", - "success": "Filter by success (true/false)", - "cached": "Filter by cache hit (true/false)", - "feedback": "Filter by feedback rating (0 or 1)", - "search": "Free-text search across log content", - "order_by": "Sort field: created_at, provider, model, model_type, success, or cached", - "order_by_direction": "Sort direction (asc or desc)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20, max 50)", - }, - Required: []string{"gateway_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_ai_gateway_log"), - Description: "Get a single AI Gateway log entry with full metadata: tokens_in, tokens_out, cost, model, provider, duration, cached, status. Use after list_ai_gateway_logs to drill into a specific request.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "gateway_id": "AI Gateway identifier", - "log_id": "Log entry identifier", - }, - Required: []string{"gateway_id", "log_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_ai_gateway_log_request"), - Description: "Get the raw request body (prompt, messages, parameters) sent to the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the exact prompt that produced a response.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "gateway_id": "AI Gateway identifier", - "log_id": "Log entry identifier", - }, - Required: []string{"gateway_id", "log_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_ai_gateway_log_response"), - Description: "Get the raw response body (completion, choices, usage) returned by the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the model's reply or error.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "gateway_id": "AI Gateway identifier", - "log_id": "Log entry identifier", - }, - Required: []string{"gateway_id", "log_id"}, - }, - - // ── Workers AI ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_ai_models"), - Description: "List Workers AI models available to run on Cloudflare's edge. Workers AI hosts open-source LLMs, embeddings, image, and speech models. Start here to discover model ids (e.g. @cf/meta/llama-3.1-8b-instruct) before calling run_ai_model.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "search": "Free-text search filter (e.g. llama, embedding)", - "task": "Filter by task type (e.g. Text Generation, Text Embeddings, Speech Recognition)", - "source": "Filter by source (1 for first-party, 2 for Hugging Face)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_run_ai_model"), - Description: "Run inference on a Workers AI model — text generation, embeddings, classification, translation, speech-to-text, image generation. Pass either `prompt` (string) for simple text-in, or `body` (object) for the model's full request schema (messages, input, image, audio, etc.). Use after list_ai_models.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "model_name": "Full model identifier (e.g. @cf/meta/llama-3.1-8b-instruct)", - "prompt": "Convenience: text prompt (wrapped as {\"prompt\":...}). Ignored if `body` is set.", - "body": "Full inference request body as a JSON object (model-specific schema)", - }, - Required: []string{"model_name"}, - }, - - // ── Vectorize ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_vectorize_indexes"), - Description: "List Vectorize v2 indexes. Vectorize is Cloudflare's globally distributed vector database for semantic search, RAG, and embeddings. Start here to discover index names before querying.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_vectorize_index"), - Description: "Get a Vectorize index's configuration: dimensions, distance metric, vector count. Use after list_vectorize_indexes.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "index_name": "Vectorize index name"}, - Required: []string{"index_name"}, - }, - { - Name: mcp.ToolName("cloudflare_create_vectorize_index"), - Description: "Create a new Vectorize v2 index for storing embeddings. Pick a metric (cosine, euclidean, dot-product) and dimension count (e.g. 768, 1536) that match your embedding model.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "name": "Index name", - "metric": "Distance metric: cosine, euclidean, or dot-product", - "dimensions": "Vector dimension count (e.g. 768, 1024, 1536)", - "description": "Optional human description", - }, - Required: []string{"name", "metric", "dimensions"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_vectorize_index"), - Description: "Delete a Vectorize index and all its vectors. Irreversible.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "index_name": "Vectorize index name"}, - Required: []string{"index_name"}, - }, - { - Name: mcp.ToolName("cloudflare_query_vectorize_index"), - Description: "Query a Vectorize index by vector — returns the topK nearest neighbors. Use for semantic search, RAG retrieval, recommendation lookup.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "index_name": "Vectorize index name", - "vector": "Query vector as JSON array of floats", - "topK": "Number of nearest neighbors to return (default 5)", - "returnValues": "Whether to return the matched vectors (true/false)", - "returnMetadata": "Whether to return metadata: 'none', 'indexed', or 'all'", - "namespace": "Optional namespace to scope the query", - "filter": "Optional metadata filter (JSON object)", - }, - Required: []string{"index_name", "vector"}, - }, - - // ── Queues ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_queues"), - Description: "List Cloudflare Queues. Queues are durable message queues for Workers — producer/consumer messaging, batch processing, async work. Start here to discover queue IDs.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_queue"), - Description: "Get a Queue's details: producers, consumers, message counts, settings. Use after list_queues.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "queue_id": "Queue identifier"}, - Required: []string{"queue_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_queue"), - Description: "Create a new Cloudflare Queue.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "queue_name": "Queue name", - "settings": "Optional settings map (delivery_delay, message_retention_period, etc.)", - }, - Required: []string{"queue_name"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_queue"), - Description: "Delete a Cloudflare Queue and all pending messages. Irreversible.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "queue_id": "Queue identifier"}, - Required: []string{"queue_id"}, - }, - { - Name: mcp.ToolName("cloudflare_send_queue_messages"), - Description: "Publish messages to a Cloudflare Queue. Messages is a JSON array of objects with at least a `body` field.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "queue_id": "Queue identifier", - "messages": "JSON array of message objects (each with `body` and optional `content_type`, `delay_seconds`)", - }, - Required: []string{"queue_id", "messages"}, - }, - - // ── Hyperdrive ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_hyperdrive_configs"), - Description: "List Hyperdrive configs. Hyperdrive accelerates database connections from Workers by pooling and caching at the edge. Start here to discover config IDs.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_hyperdrive_config"), - Description: "Get a Hyperdrive config's details: origin connection, caching, mTLS. Use after list_hyperdrive_configs.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "hyperdrive_id": "Hyperdrive config identifier"}, - Required: []string{"hyperdrive_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_hyperdrive_config"), - Description: "Create a Hyperdrive config that proxies and caches a database connection (Postgres, MySQL) for Workers.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "name": "Config name", - "origin": "Origin connection JSON: {scheme, host, port, database, user, password}", - "caching": "Optional caching JSON: {disabled, max_age, stale_while_revalidate}", - "mtls": "Optional mTLS JSON", - }, - Required: []string{"name", "origin"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_hyperdrive_config"), - Description: "Delete a Hyperdrive config. Workers using this config will fail until rebound.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "hyperdrive_id": "Hyperdrive config identifier"}, - Required: []string{"hyperdrive_id"}, - }, - - // ── Workers extras ─────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_worker_secrets"), - Description: "List secret names bound to a Worker script. Values are never returned by the API. Use to audit which secrets a Worker has access to.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "script_name": "Worker script name"}, - Required: []string{"script_name"}, - }, - { - Name: mcp.ToolName("cloudflare_list_worker_deployments"), - Description: "List recent deployments for a Worker script with versions, authors, timestamps. Use to inspect deploy history or find a version to roll back to.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "script_name": "Worker script name"}, - Required: []string{"script_name"}, - }, - { - Name: mcp.ToolName("cloudflare_get_worker_subdomain"), - Description: "Get the workers.dev subdomain enabled for this account (used as the default Worker URL).", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_list_worker_tails"), - Description: "List active Worker tails (live log streams) for a script. Use to find existing tails before opening a new live-log session.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "script_name": "Worker script name"}, - Required: []string{"script_name"}, - }, - - // ── Pages create/list domains ─────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_create_pages_project"), - Description: "Create a Cloudflare Pages project (static site / Jamstack app).", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "name": "Project name", - "production_branch": "Production branch (e.g. main)", - "build_config": "Optional build config JSON: {build_command, destination_dir, root_dir, web_analytics_tag}", - "source": "Optional source JSON: {type, config: {owner, repo_name, production_branch, ...}}", - "deployment_configs": "Optional deployment_configs JSON: {production, preview}", - }, - Required: []string{"name", "production_branch"}, - }, - { - Name: mcp.ToolName("cloudflare_create_pages_deployment"), - Description: "Trigger a new Pages deployment, optionally targeting a specific branch.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "project_name": "Pages project name", - "branch": "Optional branch to deploy (defaults to production)", - }, - Required: []string{"project_name"}, - }, - { - Name: mcp.ToolName("cloudflare_list_pages_domains"), - Description: "List custom domains attached to a Pages project (DNS + cert state).", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "project_name": "Pages project name"}, - Required: []string{"project_name"}, - }, - - // ── KV bulk ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_bulk_delete_kv_values"), - Description: "Delete multiple keys from a Workers KV namespace in one call (up to 10000 keys).", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "namespace_id": "KV namespace identifier", - "keys": "JSON array of key names to delete", - }, - Required: []string{"namespace_id", "keys"}, - }, - - // ── Stream ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_stream_videos"), - Description: "List videos hosted on Cloudflare Stream. Start here to discover video IDs for playback URLs, analytics, or deletion.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "after": "Show videos uploaded after this ISO 8601 timestamp", - "before": "Show videos uploaded before this ISO 8601 timestamp", - "creator": "Filter by creator id", - "status": "Filter by status (queued, inprogress, ready, error)", - "search": "Free-text search across video names", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_stream_video"), - Description: "Get a Stream video's metadata: playback URLs (HLS/DASH), thumbnail, duration, status. Use after list_stream_videos.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "identifier": "Stream video identifier"}, - Required: []string{"identifier"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_stream_video"), - Description: "Delete a Stream video. Irreversible.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "identifier": "Stream video identifier"}, - Required: []string{"identifier"}, - }, - - // ── Images ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_images"), - Description: "List images hosted on Cloudflare Images. Start here to discover image IDs for delivery URLs or deletion.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 50)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_image"), - Description: "Get a Cloudflare Images record: variants, metadata, upload timestamp.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "image_id": "Image identifier"}, - Required: []string{"image_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_image"), - Description: "Delete an image from Cloudflare Images. Irreversible.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "image_id": "Image identifier"}, - Required: []string{"image_id"}, - }, - - // ── Zero Trust Access ──────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_access_apps"), - Description: "List Cloudflare Zero Trust Access applications (apps protected by Cloudflare Access). Start here to discover app IDs before listing policies or auditing access.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 25)", - }, - }, - { - Name: mcp.ToolName("cloudflare_list_access_app_policies"), - Description: "List Access policies bound to a specific application. Shows who can access the app (groups, emails, IPs, IdPs, mTLS). Use after list_access_apps.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "app_id": "Access application identifier"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_access_identity_providers"), - Description: "List configured Access identity providers (Google, Okta, Azure AD, SAML, OIDC, GitHub, etc.).", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - - // ── Cloudflared Tunnels ───────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_tunnels"), - Description: "List Cloudflared tunnels for an account. Tunnels expose private origins to Cloudflare without inbound ports. Start here to discover tunnel IDs and health.", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "name": "Filter by tunnel name", - "status": "Filter by status: healthy, degraded, down, inactive", - "include_deleted": "Include deleted tunnels (true/false)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_tunnel"), - Description: "Get a Cloudflared tunnel's details: name, status, connections, created/deleted timestamps. Use after list_tunnels.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "tunnel_id": "Tunnel identifier"}, - Required: []string{"tunnel_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_tunnel"), - Description: "Delete a Cloudflared tunnel. Origins behind it become unreachable.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "tunnel_id": "Tunnel identifier"}, - Required: []string{"tunnel_id"}, - }, - - // ── Email Routing ──────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_email_routing_rules"), - Description: "List Email Routing rules for a zone (which incoming addresses forward where). Start here for email routing config audits.", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_list_email_routing_addresses"), - Description: "List verified destination email addresses for Email Routing (account-scoped).", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "verified": "Filter by verified status (true/false)", - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_email_routing_settings"), - Description: "Get Email Routing settings for a zone (enabled state, MX/SPF records, skip_wizard).", - Parameters: map[string]string{"zone_id": "Zone identifier"}, - Required: []string{"zone_id"}, - }, - - // ── Logpush ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_logpush_jobs"), - Description: "List Logpush jobs for an account (HTTP requests, firewall events, Workers traces, etc. exported to S3/GCS/Azure/Sumo/Datadog/Splunk/New Relic/R2). Start here to inspect log-export pipelines.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_get_logpush_job"), - Description: "Get a Logpush job's config: dataset, destination, frequency, filters, last error. Use after list_logpush_jobs.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)", "job_id": "Logpush job ID (integer)"}, - Required: []string{"job_id"}, - }, - { - Name: mcp.ToolName("cloudflare_create_logpush_job"), - Description: "Create a Logpush job to export Cloudflare logs to an external sink (S3, GCS, Azure, R2, Splunk, Datadog, New Relic, Sumo Logic, HTTP).", - Parameters: map[string]string{ - "account_id": "Account identifier (defaults to configured account_id)", - "dataset": "Log dataset (e.g. http_requests, firewall_events, workers_trace_events)", - "destination_conf": "Destination connection string (e.g. s3://bucket/path?region=...)", - "name": "Optional job name", - "frequency": "Optional frequency: high or low", - "logpull_options": "Optional logpull options string (fields, timestamps, etc.)", - "output_options": "Optional output options map (field_names, timestamp_format, batch sizing)", - "filter": "Optional log filter expression", - "enabled": "Whether the job is enabled (true/false)", - }, - Required: []string{"dataset", "destination_conf"}, - }, - - // ── Page Rules ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_page_rules"), - Description: "List Page Rules for a zone (URL-pattern rules for cache, redirects, headers, security). Start here for legacy page-rule audits before migrating to Rulesets.", - Parameters: map[string]string{ - "zone_id": "Zone identifier", - "status": "Filter by status (active, disabled)", - "order": "Sort field (priority, status)", - "direction": "Sort direction (asc, desc)", - }, - Required: []string{"zone_id"}, - }, - { - Name: mcp.ToolName("cloudflare_get_page_rule"), - Description: "Get a Page Rule's targets and actions. Use after list_page_rules.", - Parameters: map[string]string{"zone_id": "Zone identifier", "pagerule_id": "Page Rule identifier"}, - Required: []string{"zone_id", "pagerule_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_page_rule"), - Description: "Delete a Page Rule.", - Parameters: map[string]string{"zone_id": "Zone identifier", "pagerule_id": "Page Rule identifier"}, - Required: []string{"zone_id", "pagerule_id"}, - }, - - // ── Notifications (Alerting v3) ───────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_notification_policies"), - Description: "List notification (alerting) policies — what conditions trigger emails, PagerDuty, webhooks for an account.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - { - Name: mcp.ToolName("cloudflare_list_notification_webhooks"), - Description: "List configured webhook destinations for Cloudflare notifications.", - Parameters: map[string]string{"account_id": "Account identifier (defaults to configured account_id)"}, - }, - - // ── User API Tokens ───────────────────────────────────────────── - { - Name: mcp.ToolName("cloudflare_list_api_tokens"), - Description: "List Cloudflare API tokens issued under the authenticated user. Start here to audit which tokens exist, their scopes, and their last-used timestamp.", - Parameters: map[string]string{ - "page": "Page number (default 1)", - "per_page": "Results per page (default 20)", - }, - }, - { - Name: mcp.ToolName("cloudflare_get_api_token"), - Description: "Get a Cloudflare API token's policy details: permissions, IP/time restrictions, expiry, last_used_on. Use after list_api_tokens.", - Parameters: map[string]string{"token_id": "API token identifier"}, - Required: []string{"token_id"}, - }, - { - Name: mcp.ToolName("cloudflare_delete_api_token"), - Description: "Revoke (delete) a Cloudflare API token. Any client using it will start receiving 401s.", - Parameters: map[string]string{"token_id": "API token identifier"}, - Required: []string{"token_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/cloudflare/tools.yaml b/integrations/cloudflare/tools.yaml new file mode 100644 index 00000000..4588f18f --- /dev/null +++ b/integrations/cloudflare/tools.yaml @@ -0,0 +1,1093 @@ +version: 1 +tools: + cloudflare_list_zones: + description: "List Cloudflare zones (domains). Start here for DNS management, CDN configuration, and website performance. Zones represent domains registered with Cloudflare." + parameters: + name: + description: "Zone name filter (exact domain match, e.g. example.com)" + status: + description: "Zone status filter (active, pending, initializing, moved, deleted, deactivated, read_only)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20, max 50)" + + cloudflare_get_zone: + description: "Get details for a specific Cloudflare zone including status, nameservers, and settings. Use after list_zones." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_create_zone: + description: "Add a new domain (zone) to Cloudflare. Requires account_id." + parameters: + name: + description: "Domain name (e.g. example.com)" + required: true + account_id: + description: "Account identifier (defaults to configured account_id)" + type: + description: "Zone type: full (default) or partial" + jump_start: + description: "Auto-fetch DNS records (true/false, default false)" + + cloudflare_edit_zone: + description: "Update zone settings like paused status or vanity nameservers. Use after get_zone." + parameters: + zone_id: + description: "Zone identifier" + required: true + paused: + description: "Pause Cloudflare for this zone (true/false)" + plan: + description: "Plan identifier to change zone plan" + + cloudflare_delete_zone: + description: "Remove a zone (domain) from Cloudflare. Irreversible." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_purge_cache: + description: "Purge cached content for a Cloudflare zone. Purge everything or specific URLs/tags/hosts. Use for cache invalidation after deployments." + parameters: + zone_id: + description: "Zone identifier" + required: true + purge_everything: + description: "Purge all cached content (true/false)" + files: + description: "JSON array of URLs to purge (alternative to purge_everything)" + tags: + description: "Comma-separated cache tags to purge" + hosts: + description: "Comma-separated hostnames to purge" + + cloudflare_list_dns_records: + description: "List DNS records for a Cloudflare zone. View A, AAAA, CNAME, MX, TXT, NS, and other record types. Start here for DNS management and troubleshooting." + parameters: + zone_id: + description: "Zone identifier" + required: true + type: + description: "DNS record type filter (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)" + name: + description: "DNS record name filter (e.g. subdomain.example.com)" + content: + description: "DNS record content filter (e.g. IP address)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20, max 100)" + + cloudflare_get_dns_record: + description: "Get details for a specific DNS record. Use after list_dns_records." + parameters: + zone_id: + description: "Zone identifier" + required: true + record_id: + description: "DNS record identifier" + required: true + + cloudflare_create_dns_record: + description: "Create a new DNS record in a Cloudflare zone. Add A, AAAA, CNAME, MX, TXT records and more." + parameters: + zone_id: + description: "Zone identifier" + required: true + type: + description: "DNS record type (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)" + required: true + name: + description: "DNS record name (e.g. subdomain or @ for root)" + required: true + content: + description: "DNS record content (e.g. IP address, target domain)" + required: true + ttl: + description: "TTL in seconds (1 = automatic, default 1)" + proxied: + description: "Whether traffic is proxied through Cloudflare (true/false)" + priority: + description: "MX/SRV record priority" + + cloudflare_update_dns_record: + description: "Update an existing DNS record. Use after list_dns_records to get record_id." + parameters: + zone_id: + description: "Zone identifier" + required: true + record_id: + description: "DNS record identifier" + required: true + type: + description: "DNS record type" + required: true + name: + description: "DNS record name" + required: true + content: + description: "DNS record content" + required: true + ttl: + description: "TTL in seconds (1 = automatic)" + proxied: + description: "Whether traffic is proxied through Cloudflare (true/false)" + priority: + description: "MX/SRV record priority" + + cloudflare_delete_dns_record: + description: "Delete a DNS record from a Cloudflare zone." + parameters: + zone_id: + description: "Zone identifier" + required: true + record_id: + description: "DNS record identifier" + required: true + + cloudflare_list_workers: + description: "List Cloudflare Workers scripts. View serverless functions, edge computing workers, and deployed scripts. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_worker: + description: "Get metadata for a specific Cloudflare Worker script including bindings, routes, and compatibility settings. Use after list_workers." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + script_name: + description: "Worker script name" + required: true + + cloudflare_delete_worker: + description: "Delete a Cloudflare Worker script." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + script_name: + description: "Worker script name" + required: true + + cloudflare_list_worker_routes: + description: "List Worker routes for a zone. Shows URL patterns mapped to Worker scripts." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_list_pages_projects: + description: "List Cloudflare Pages projects. View static sites, Jamstack deployments, and frontend projects hosted on Pages. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_pages_project: + description: "Get details for a Cloudflare Pages project including build config, domains, and deployment settings. Use after list_pages_projects." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + + cloudflare_list_pages_deployments: + description: "List deployments for a Cloudflare Pages project. View deploy history, build status, and rollback targets." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + + cloudflare_get_pages_deployment: + description: "Get details for a specific Pages deployment including build logs and environment. Use after list_pages_deployments." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + deployment_id: + description: "Deployment identifier" + required: true + + cloudflare_delete_pages_deployment: + description: "Delete a specific Pages deployment." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + deployment_id: + description: "Deployment identifier" + required: true + + cloudflare_rollback_pages_deployment: + description: "Rollback a Pages project to a previous deployment. Use after list_pages_deployments to find deployment_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + deployment_id: + description: "Deployment identifier to rollback to" + required: true + + cloudflare_list_r2_buckets: + description: "List R2 object storage buckets. Cloudflare R2 is S3-compatible storage with zero egress fees. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_create_r2_bucket: + description: "Create a new R2 object storage bucket." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Bucket name" + required: true + + cloudflare_delete_r2_bucket: + description: "Delete an R2 object storage bucket. Bucket must be empty." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Bucket name" + required: true + + cloudflare_list_kv_namespaces: + description: "List Workers KV namespaces. KV is Cloudflare's global key-value store for edge data. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_create_kv_namespace: + description: "Create a new Workers KV namespace." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + title: + description: "Namespace title" + required: true + + cloudflare_delete_kv_namespace: + description: "Delete a Workers KV namespace and all its keys." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + + cloudflare_list_kv_keys: + description: "List keys in a Workers KV namespace. Returns key names with optional metadata." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + prefix: + description: "Key prefix filter" + limit: + description: "Maximum keys to return (default 1000)" + cursor: + description: "Pagination cursor from previous response" + + cloudflare_get_kv_value: + description: "Read a value from Workers KV by key. Returns the stored value as text." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + key_name: + description: "Key name to read" + required: true + + cloudflare_put_kv_value: + description: "Write a key-value pair to Workers KV." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + key_name: + description: "Key name" + required: true + value: + description: "Value to store" + required: true + metadata: + description: "JSON metadata object to attach to the key" + + cloudflare_delete_kv_value: + description: "Delete a key-value pair from Workers KV." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + key_name: + description: "Key name to delete" + required: true + + cloudflare_list_d1_databases: + description: "List D1 SQL databases. D1 is Cloudflare's serverless SQLite database for Workers. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_d1_database: + description: "Get details for a specific D1 database including size and table count. Use after list_d1_databases." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + database_id: + description: "D1 database identifier" + required: true + + cloudflare_create_d1_database: + description: "Create a new D1 SQL database." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Database name" + required: true + + cloudflare_delete_d1_database: + description: "Delete a D1 SQL database and all its data. Irreversible." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + database_id: + description: "D1 database identifier" + required: true + + cloudflare_query_d1_database: + description: "Execute a SQL query against a D1 database. Supports SELECT, INSERT, UPDATE, DELETE and DDL. Use parameterized queries with params array for safety." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + database_id: + description: "D1 database identifier" + required: true + sql: + description: "SQL query to execute" + required: true + params: + description: "JSON array of query parameters for prepared statements" + + cloudflare_list_waf_rulesets: + description: "List WAF (Web Application Firewall) rulesets for a zone. View security rules protecting against attacks, bots, and threats." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_get_waf_ruleset: + description: "Get details for a specific WAF ruleset including individual rules and their actions. Use after list_waf_rulesets." + parameters: + zone_id: + description: "Zone identifier" + required: true + ruleset_id: + description: "Ruleset identifier" + required: true + + cloudflare_list_load_balancers: + description: "List load balancers for a zone. View traffic distribution, health checks, and failover configuration." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_get_load_balancer: + description: "Get details for a specific load balancer including pools, rules, and session affinity. Use after list_load_balancers." + parameters: + zone_id: + description: "Zone identifier" + required: true + lb_id: + description: "Load balancer identifier" + required: true + + cloudflare_list_lb_pools: + description: "List load balancer pools (origin server groups). View pool health, origins, and traffic steering. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_lb_pool: + description: "Get details for a specific load balancer pool including origins and health check results. Use after list_lb_pools." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + pool_id: + description: "Pool identifier" + required: true + + cloudflare_list_lb_monitors: + description: "List load balancer health monitors. View health check configurations for origin pools. Requires account_id." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_zone_analytics: + description: "Get zone analytics dashboard data including requests, bandwidth, threats, and page views. Provides traffic overview and performance metrics for a Cloudflare zone." + parameters: + zone_id: + description: "Zone identifier" + required: true + since: + description: "Start time (ISO 8601 or relative like -1440 for minutes ago, default -1440)" + until: + description: "End time (ISO 8601 or relative, default 0 for now)" + + cloudflare_list_accounts: + description: "List Cloudflare accounts the API token has access to." + parameters: + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_account: + description: "Get details for a specific Cloudflare account. Use after list_accounts." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_list_account_members: + description: "List members of a Cloudflare account with their roles and permissions." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_list_ai_gateways: + description: "List Cloudflare AI Gateways for an account. AI Gateway proxies LLM traffic to OpenAI, Anthropic, Workers AI, and other providers with caching, rate limiting, logging, and cost tracking. Start here to discover gateway IDs before pulling logs, token usage, or spend." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_ai_gateway: + description: "Get a Cloudflare AI Gateway's configuration: cache TTL, rate limits, log collection, spend limits, authentication, DLP, and guardrails. Use after list_ai_gateways." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + gateway_id: + description: "AI Gateway identifier" + required: true + + cloudflare_list_ai_gateway_logs: + description: "List AI Gateway request logs with per-request token usage (tokens_in, tokens_out), cost, latency, model, provider, cache hits, and success/error status. Use this to audit LLM spend, debug failing inferences, or attribute token consumption to a model or provider. Filter by date, provider, model, success, cached, or feedback." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + gateway_id: + description: "AI Gateway identifier" + required: true + start_date: + description: "Start of time range (ISO 8601, e.g. 2024-01-01T00:00:00Z)" + end_date: + description: "End of time range (ISO 8601)" + provider: + description: "Filter by LLM provider (e.g. openai, anthropic, workers-ai)" + model: + description: "Filter by model id (e.g. gpt-4o, claude-3-5-sonnet)" + success: + description: "Filter by success (true/false)" + cached: + description: "Filter by cache hit (true/false)" + feedback: + description: "Filter by feedback rating (0 or 1)" + search: + description: "Free-text search across log content" + order_by: + description: "Sort field: created_at, provider, model, model_type, success, or cached" + order_by_direction: + description: "Sort direction (asc or desc)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20, max 50)" + + cloudflare_get_ai_gateway_log: + description: "Get a single AI Gateway log entry with full metadata: tokens_in, tokens_out, cost, model, provider, duration, cached, status. Use after list_ai_gateway_logs to drill into a specific request." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + gateway_id: + description: "AI Gateway identifier" + required: true + log_id: + description: "Log entry identifier" + required: true + + cloudflare_get_ai_gateway_log_request: + description: "Get the raw request body (prompt, messages, parameters) sent to the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the exact prompt that produced a response." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + gateway_id: + description: "AI Gateway identifier" + required: true + log_id: + description: "Log entry identifier" + required: true + + cloudflare_get_ai_gateway_log_response: + description: "Get the raw response body (completion, choices, usage) returned by the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the model's reply or error." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + gateway_id: + description: "AI Gateway identifier" + required: true + log_id: + description: "Log entry identifier" + required: true + + cloudflare_list_ai_models: + description: "List Workers AI models available to run on Cloudflare's edge. Workers AI hosts open-source LLMs, embeddings, image, and speech models. Start here to discover model ids (e.g. @cf/meta/llama-3.1-8b-instruct) before calling run_ai_model." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + search: + description: "Free-text search filter (e.g. llama, embedding)" + task: + description: "Filter by task type (e.g. Text Generation, Text Embeddings, Speech Recognition)" + source: + description: "Filter by source (1 for first-party, 2 for Hugging Face)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_run_ai_model: + description: "Run inference on a Workers AI model — text generation, embeddings, classification, translation, speech-to-text, image generation. Pass either `prompt` (string) for simple text-in, or `body` (object) for the model's full request schema (messages, input, image, audio, etc.). Use after list_ai_models." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + model_name: + description: "Full model identifier (e.g. @cf/meta/llama-3.1-8b-instruct)" + required: true + prompt: + description: "Convenience: text prompt (wrapped as {\"prompt\":...}). Ignored if `body` is set." + body: + description: "Full inference request body as a JSON object (model-specific schema)" + + cloudflare_list_vectorize_indexes: + description: "List Vectorize v2 indexes. Vectorize is Cloudflare's globally distributed vector database for semantic search, RAG, and embeddings. Start here to discover index names before querying." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_vectorize_index: + description: "Get a Vectorize index's configuration: dimensions, distance metric, vector count. Use after list_vectorize_indexes." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + index_name: + description: "Vectorize index name" + required: true + + cloudflare_create_vectorize_index: + description: "Create a new Vectorize v2 index for storing embeddings. Pick a metric (cosine, euclidean, dot-product) and dimension count (e.g. 768, 1536) that match your embedding model." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Index name" + required: true + metric: + description: "Distance metric: cosine, euclidean, or dot-product" + required: true + dimensions: + description: "Vector dimension count (e.g. 768, 1024, 1536)" + required: true + description: + description: "Optional human description" + + cloudflare_delete_vectorize_index: + description: "Delete a Vectorize index and all its vectors. Irreversible." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + index_name: + description: "Vectorize index name" + required: true + + cloudflare_query_vectorize_index: + description: "Query a Vectorize index by vector — returns the topK nearest neighbors. Use for semantic search, RAG retrieval, recommendation lookup." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + index_name: + description: "Vectorize index name" + required: true + vector: + description: "Query vector as JSON array of floats" + required: true + topK: + description: "Number of nearest neighbors to return (default 5)" + returnValues: + description: "Whether to return the matched vectors (true/false)" + returnMetadata: + description: "Whether to return metadata: 'none', 'indexed', or 'all'" + namespace: + description: "Optional namespace to scope the query" + filter: + description: "Optional metadata filter (JSON object)" + + cloudflare_list_queues: + description: "List Cloudflare Queues. Queues are durable message queues for Workers — producer/consumer messaging, batch processing, async work. Start here to discover queue IDs." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_queue: + description: "Get a Queue's details: producers, consumers, message counts, settings. Use after list_queues." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + queue_id: + description: "Queue identifier" + required: true + + cloudflare_create_queue: + description: "Create a new Cloudflare Queue." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + queue_name: + description: "Queue name" + required: true + settings: + description: "Optional settings map (delivery_delay, message_retention_period, etc.)" + + cloudflare_delete_queue: + description: "Delete a Cloudflare Queue and all pending messages. Irreversible." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + queue_id: + description: "Queue identifier" + required: true + + cloudflare_send_queue_messages: + description: "Publish messages to a Cloudflare Queue. Messages is a JSON array of objects with at least a `body` field." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + queue_id: + description: "Queue identifier" + required: true + messages: + description: "JSON array of message objects (each with `body` and optional `content_type`, `delay_seconds`)" + required: true + + cloudflare_list_hyperdrive_configs: + description: "List Hyperdrive configs. Hyperdrive accelerates database connections from Workers by pooling and caching at the edge. Start here to discover config IDs." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_hyperdrive_config: + description: "Get a Hyperdrive config's details: origin connection, caching, mTLS. Use after list_hyperdrive_configs." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + hyperdrive_id: + description: "Hyperdrive config identifier" + required: true + + cloudflare_create_hyperdrive_config: + description: "Create a Hyperdrive config that proxies and caches a database connection (Postgres, MySQL) for Workers." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Config name" + required: true + origin: + description: "Origin connection JSON: {scheme, host, port, database, user, password}" + required: true + caching: + description: "Optional caching JSON: {disabled, max_age, stale_while_revalidate}" + mtls: + description: "Optional mTLS JSON" + + cloudflare_delete_hyperdrive_config: + description: "Delete a Hyperdrive config. Workers using this config will fail until rebound." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + hyperdrive_id: + description: "Hyperdrive config identifier" + required: true + + cloudflare_list_worker_secrets: + description: "List secret names bound to a Worker script. Values are never returned by the API. Use to audit which secrets a Worker has access to." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + script_name: + description: "Worker script name" + required: true + + cloudflare_list_worker_deployments: + description: "List recent deployments for a Worker script with versions, authors, timestamps. Use to inspect deploy history or find a version to roll back to." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + script_name: + description: "Worker script name" + required: true + + cloudflare_get_worker_subdomain: + description: "Get the workers.dev subdomain enabled for this account (used as the default Worker URL)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_list_worker_tails: + description: "List active Worker tails (live log streams) for a script. Use to find existing tails before opening a new live-log session." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + script_name: + description: "Worker script name" + required: true + + cloudflare_create_pages_project: + description: "Create a Cloudflare Pages project (static site / Jamstack app)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Project name" + required: true + production_branch: + description: "Production branch (e.g. main)" + required: true + build_config: + description: "Optional build config JSON: {build_command, destination_dir, root_dir, web_analytics_tag}" + source: + description: "Optional source JSON: {type, config: {owner, repo_name, production_branch, ...}}" + deployment_configs: + description: "Optional deployment_configs JSON: {production, preview}" + + cloudflare_create_pages_deployment: + description: "Trigger a new Pages deployment, optionally targeting a specific branch." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + branch: + description: "Optional branch to deploy (defaults to production)" + + cloudflare_list_pages_domains: + description: "List custom domains attached to a Pages project (DNS + cert state)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + project_name: + description: "Pages project name" + required: true + + cloudflare_bulk_delete_kv_values: + description: "Delete multiple keys from a Workers KV namespace in one call (up to 10000 keys)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + namespace_id: + description: "KV namespace identifier" + required: true + keys: + description: "JSON array of key names to delete" + required: true + + cloudflare_list_stream_videos: + description: "List videos hosted on Cloudflare Stream. Start here to discover video IDs for playback URLs, analytics, or deletion." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + after: + description: "Show videos uploaded after this ISO 8601 timestamp" + before: + description: "Show videos uploaded before this ISO 8601 timestamp" + creator: + description: "Filter by creator id" + status: + description: "Filter by status (queued, inprogress, ready, error)" + search: + description: "Free-text search across video names" + + cloudflare_get_stream_video: + description: "Get a Stream video's metadata: playback URLs (HLS/DASH), thumbnail, duration, status. Use after list_stream_videos." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + identifier: + description: "Stream video identifier" + required: true + + cloudflare_delete_stream_video: + description: "Delete a Stream video. Irreversible." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + identifier: + description: "Stream video identifier" + required: true + + cloudflare_list_images: + description: "List images hosted on Cloudflare Images. Start here to discover image IDs for delivery URLs or deletion." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 50)" + + cloudflare_get_image: + description: "Get a Cloudflare Images record: variants, metadata, upload timestamp." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + image_id: + description: "Image identifier" + required: true + + cloudflare_delete_image: + description: "Delete an image from Cloudflare Images. Irreversible." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + image_id: + description: "Image identifier" + required: true + + cloudflare_list_access_apps: + description: "List Cloudflare Zero Trust Access applications (apps protected by Cloudflare Access). Start here to discover app IDs before listing policies or auditing access." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 25)" + + cloudflare_list_access_app_policies: + description: "List Access policies bound to a specific application. Shows who can access the app (groups, emails, IPs, IdPs, mTLS). Use after list_access_apps." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + app_id: + description: "Access application identifier" + required: true + + cloudflare_list_access_identity_providers: + description: "List configured Access identity providers (Google, Okta, Azure AD, SAML, OIDC, GitHub, etc.)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_list_tunnels: + description: "List Cloudflared tunnels for an account. Tunnels expose private origins to Cloudflare without inbound ports. Start here to discover tunnel IDs and health." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + name: + description: "Filter by tunnel name" + status: + description: "Filter by status: healthy, degraded, down, inactive" + include_deleted: + description: "Include deleted tunnels (true/false)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_tunnel: + description: "Get a Cloudflared tunnel's details: name, status, connections, created/deleted timestamps. Use after list_tunnels." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + tunnel_id: + description: "Tunnel identifier" + required: true + + cloudflare_delete_tunnel: + description: "Delete a Cloudflared tunnel. Origins behind it become unreachable." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + tunnel_id: + description: "Tunnel identifier" + required: true + + cloudflare_list_email_routing_rules: + description: "List Email Routing rules for a zone (which incoming addresses forward where). Start here for email routing config audits." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_list_email_routing_addresses: + description: "List verified destination email addresses for Email Routing (account-scoped)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + verified: + description: "Filter by verified status (true/false)" + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_email_routing_settings: + description: "Get Email Routing settings for a zone (enabled state, MX/SPF records, skip_wizard)." + parameters: + zone_id: + description: "Zone identifier" + required: true + + cloudflare_list_logpush_jobs: + description: "List Logpush jobs for an account (HTTP requests, firewall events, Workers traces, etc. exported to S3/GCS/Azure/Sumo/Datadog/Splunk/New Relic/R2). Start here to inspect log-export pipelines." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_get_logpush_job: + description: "Get a Logpush job's config: dataset, destination, frequency, filters, last error. Use after list_logpush_jobs." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + job_id: + description: "Logpush job ID (integer)" + required: true + + cloudflare_create_logpush_job: + description: "Create a Logpush job to export Cloudflare logs to an external sink (S3, GCS, Azure, R2, Splunk, Datadog, New Relic, Sumo Logic, HTTP)." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + dataset: + description: "Log dataset (e.g. http_requests, firewall_events, workers_trace_events)" + required: true + destination_conf: + description: "Destination connection string (e.g. s3://bucket/path?region=...)" + required: true + name: + description: "Optional job name" + frequency: + description: "Optional frequency: high or low" + logpull_options: + description: "Optional logpull options string (fields, timestamps, etc.)" + output_options: + description: "Optional output options map (field_names, timestamp_format, batch sizing)" + filter: + description: "Optional log filter expression" + enabled: + description: "Whether the job is enabled (true/false)" + + cloudflare_list_page_rules: + description: "List Page Rules for a zone (URL-pattern rules for cache, redirects, headers, security). Start here for legacy page-rule audits before migrating to Rulesets." + parameters: + zone_id: + description: "Zone identifier" + required: true + status: + description: "Filter by status (active, disabled)" + order: + description: "Sort field (priority, status)" + direction: + description: "Sort direction (asc, desc)" + + cloudflare_get_page_rule: + description: "Get a Page Rule's targets and actions. Use after list_page_rules." + parameters: + zone_id: + description: "Zone identifier" + required: true + pagerule_id: + description: "Page Rule identifier" + required: true + + cloudflare_delete_page_rule: + description: "Delete a Page Rule." + parameters: + zone_id: + description: "Zone identifier" + required: true + pagerule_id: + description: "Page Rule identifier" + required: true + + cloudflare_list_notification_policies: + description: "List notification (alerting) policies — what conditions trigger emails, PagerDuty, webhooks for an account." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_list_notification_webhooks: + description: "List configured webhook destinations for Cloudflare notifications." + parameters: + account_id: + description: "Account identifier (defaults to configured account_id)" + + cloudflare_list_api_tokens: + description: "List Cloudflare API tokens issued under the authenticated user. Start here to audit which tokens exist, their scopes, and their last-used timestamp." + parameters: + page: + description: "Page number (default 1)" + per_page: + description: "Results per page (default 20)" + + cloudflare_get_api_token: + description: "Get a Cloudflare API token's policy details: permissions, IP/time restrictions, expiry, last_used_on. Use after list_api_tokens." + parameters: + token_id: + description: "API token identifier" + required: true + + cloudflare_delete_api_token: + description: "Revoke (delete) a Cloudflare API token. Any client using it will start receiving 401s." + parameters: + token_id: + description: "API token identifier" + required: true diff --git a/integrations/confluence/tools.go b/integrations/confluence/tools.go index fe54ce05..fe5b3e05 100644 --- a/integrations/confluence/tools.go +++ b/integrations/confluence/tools.go @@ -1,95 +1,12 @@ package confluence -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Spaces ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("confluence_list_spaces"), Description: "List all accessible Confluence spaces. Start here to find space IDs for other operations", - Parameters: map[string]string{"cursor": "Pagination cursor from previous response", "limit": "Max results per page (default 25, max 250)", "type": "Filter by type: global, personal", "status": "Filter by status: current, archived"}, - }, - { - Name: mcp.ToolName("confluence_get_space"), Description: "Get details of a specific space by ID", - Parameters: map[string]string{"space_id": "Space ID"}, - Required: []string{"space_id"}, - }, - { - Name: mcp.ToolName("confluence_search"), Description: "Search Confluence content using CQL (Confluence Query Language). Supports pages, blog posts, comments, and attachments", - Parameters: map[string]string{"cql": "CQL query (e.g., 'type=page AND space=DEV AND title~\"design doc\"')", "limit": "Max results per page (default 25, max 100)", "start": "Pagination offset (0-based)", "excerpt": "Include excerpt in results: none, highlight, indexed (default none)"}, - Required: []string{"cql"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Pages ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("confluence_list_pages"), Description: "List pages, optionally filtered by space or title", - Parameters: map[string]string{"space_id": "Filter by space ID", "title": "Filter by exact title", "status": "Filter by status: current, trashed, draft (default current)", "cursor": "Pagination cursor from previous response", "limit": "Max results per page (default 25, max 250)", "sort": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date"}, - }, - { - Name: mcp.ToolName("confluence_get_page"), Description: "Get full details of a specific page by ID, including body content", - Parameters: map[string]string{"page_id": "Page ID", "body_format": "Body format to return: storage, atlas_doc_format, view (default storage)", "version": "Specific version number to retrieve"}, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("confluence_create_page"), Description: "Create a new page in a space", - Parameters: map[string]string{"space_id": "Space ID to create page in", "title": "Page title", "body_value": "Page body content", "body_format": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)", "parent_id": "Parent page ID for nesting", "status": "Page status: current, draft (default current)"}, - Required: []string{"space_id", "title", "body_value"}, - }, - { - Name: mcp.ToolName("confluence_update_page"), Description: "Update an existing page. Requires the current version number (use confluence_get_page to find it)", - Parameters: map[string]string{"page_id": "Page ID", "title": "New page title", "body_value": "New page body content", "body_format": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)", "version_number": "New version number (must be current version + 1)", "version_message": "Version change message", "status": "Page status: current, draft"}, - Required: []string{"page_id", "title", "body_value", "version_number"}, - }, - { - Name: mcp.ToolName("confluence_delete_page"), Description: "Delete a page by ID", - Parameters: map[string]string{"page_id": "Page ID"}, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("confluence_get_page_children"), Description: "Get child pages of a specific page", - Parameters: map[string]string{"page_id": "Parent page ID", "cursor": "Pagination cursor from previous response", "limit": "Max results per page (default 25, max 250)", "sort": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date"}, - Required: []string{"page_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Blog Posts ────────────────────────────────────────────────── - { - Name: mcp.ToolName("confluence_list_blog_posts"), Description: "List blog posts, optionally filtered by space or title", - Parameters: map[string]string{"space_id": "Filter by space ID", "title": "Filter by exact title", "status": "Filter by status: current, trashed, draft (default current)", "cursor": "Pagination cursor from previous response", "limit": "Max results per page (default 25, max 250)", "sort": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date"}, - }, - { - Name: mcp.ToolName("confluence_get_blog_post"), Description: "Get full details of a specific blog post by ID, including body content", - Parameters: map[string]string{"blogpost_id": "Blog post ID", "body_format": "Body format to return: storage, atlas_doc_format, view (default storage)", "version": "Specific version number to retrieve"}, - Required: []string{"blogpost_id"}, - }, - { - Name: mcp.ToolName("confluence_create_blog_post"), Description: "Create a new blog post in a space", - Parameters: map[string]string{"space_id": "Space ID to create blog post in", "title": "Blog post title", "body_value": "Blog post body content", "body_format": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)", "status": "Blog post status: current, draft (default current)"}, - Required: []string{"space_id", "title", "body_value"}, - }, - { - Name: mcp.ToolName("confluence_update_blog_post"), Description: "Update an existing blog post. Requires the current version number (use confluence_get_blog_post to find it)", - Parameters: map[string]string{"blogpost_id": "Blog post ID", "title": "New blog post title", "body_value": "New blog post body content", "body_format": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)", "version_number": "New version number (must be current version + 1)", "version_message": "Version change message", "status": "Blog post status: current, draft"}, - Required: []string{"blogpost_id", "title", "body_value", "version_number"}, - }, - { - Name: mcp.ToolName("confluence_delete_blog_post"), Description: "Delete a blog post by ID", - Parameters: map[string]string{"blogpost_id": "Blog post ID"}, - Required: []string{"blogpost_id"}, - }, - - // ── Comments ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("confluence_list_comments"), Description: "List footer comments on a page or blog post", - Parameters: map[string]string{"parent_type": "Parent content type: page, blogpost", "parent_id": "Parent content ID", "cursor": "Pagination cursor from previous response", "limit": "Max results per page (default 25, max 250)"}, - Required: []string{"parent_type", "parent_id"}, - }, - { - Name: mcp.ToolName("confluence_create_comment"), Description: "Add a footer comment to a page or blog post", - Parameters: map[string]string{"parent_type": "Parent content type: page, blogpost", "parent_id": "Parent content ID", "body_value": "Comment body content", "body_format": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)"}, - Required: []string{"parent_type", "parent_id", "body_value"}, - }, - { - Name: mcp.ToolName("confluence_delete_comment"), Description: "Delete a footer comment by ID", - Parameters: map[string]string{"comment_id": "Comment ID"}, - Required: []string{"comment_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/confluence/tools.yaml b/integrations/confluence/tools.yaml new file mode 100644 index 00000000..dbab5101 --- /dev/null +++ b/integrations/confluence/tools.yaml @@ -0,0 +1,214 @@ +version: 1 +tools: + confluence_list_spaces: + description: "List all accessible Confluence spaces. Start here to find space IDs for other operations" + parameters: + cursor: + description: "Pagination cursor from previous response" + limit: + description: "Max results per page (default 25, max 250)" + type: + description: "Filter by type: global, personal" + status: + description: "Filter by status: current, archived" + confluence_get_space: + description: "Get details of a specific space by ID" + parameters: + space_id: + description: "Space ID" + required: true + confluence_search: + description: "Search Confluence content using CQL (Confluence Query Language). Supports pages, blog posts, comments, and attachments" + parameters: + cql: + description: 'CQL query (e.g., ''type=page AND space=DEV AND title~"design doc"'')' + required: true + limit: + description: "Max results per page (default 25, max 100)" + start: + description: "Pagination offset (0-based)" + excerpt: + description: "Include excerpt in results: none, highlight, indexed (default none)" + confluence_list_pages: + description: "List pages, optionally filtered by space or title" + parameters: + space_id: + description: "Filter by space ID" + title: + description: "Filter by exact title" + status: + description: "Filter by status: current, trashed, draft (default current)" + cursor: + description: "Pagination cursor from previous response" + limit: + description: "Max results per page (default 25, max 250)" + sort: + description: "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + confluence_get_page: + description: "Get full details of a specific page by ID, including body content" + parameters: + page_id: + description: "Page ID" + required: true + body_format: + description: "Body format to return: storage, atlas_doc_format, view (default storage)" + version: + description: "Specific version number to retrieve" + confluence_create_page: + description: "Create a new page in a space" + parameters: + space_id: + description: "Space ID to create page in" + required: true + title: + description: "Page title" + required: true + body_value: + description: "Page body content" + required: true + body_format: + description: "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + parent_id: + description: "Parent page ID for nesting" + status: + description: "Page status: current, draft (default current)" + confluence_update_page: + description: "Update an existing page. Requires the current version number (use confluence_get_page to find it)" + parameters: + page_id: + description: "Page ID" + required: true + title: + description: "New page title" + required: true + body_value: + description: "New page body content" + required: true + body_format: + description: "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + version_number: + description: "New version number (must be current version + 1)" + required: true + version_message: + description: "Version change message" + status: + description: "Page status: current, draft" + confluence_delete_page: + description: "Delete a page by ID" + parameters: + page_id: + description: "Page ID" + required: true + confluence_get_page_children: + description: "Get child pages of a specific page" + parameters: + page_id: + description: "Parent page ID" + required: true + cursor: + description: "Pagination cursor from previous response" + limit: + description: "Max results per page (default 25, max 250)" + sort: + description: "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + confluence_list_blog_posts: + description: "List blog posts, optionally filtered by space or title" + parameters: + space_id: + description: "Filter by space ID" + title: + description: "Filter by exact title" + status: + description: "Filter by status: current, trashed, draft (default current)" + cursor: + description: "Pagination cursor from previous response" + limit: + description: "Max results per page (default 25, max 250)" + sort: + description: "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + confluence_get_blog_post: + description: "Get full details of a specific blog post by ID, including body content" + parameters: + blogpost_id: + description: "Blog post ID" + required: true + body_format: + description: "Body format to return: storage, atlas_doc_format, view (default storage)" + version: + description: "Specific version number to retrieve" + confluence_create_blog_post: + description: "Create a new blog post in a space" + parameters: + space_id: + description: "Space ID to create blog post in" + required: true + title: + description: "Blog post title" + required: true + body_value: + description: "Blog post body content" + required: true + body_format: + description: "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + status: + description: "Blog post status: current, draft (default current)" + confluence_update_blog_post: + description: "Update an existing blog post. Requires the current version number (use confluence_get_blog_post to find it)" + parameters: + blogpost_id: + description: "Blog post ID" + required: true + title: + description: "New blog post title" + required: true + body_value: + description: "New blog post body content" + required: true + body_format: + description: "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + version_number: + description: "New version number (must be current version + 1)" + required: true + version_message: + description: "Version change message" + status: + description: "Blog post status: current, draft" + confluence_delete_blog_post: + description: "Delete a blog post by ID" + parameters: + blogpost_id: + description: "Blog post ID" + required: true + confluence_list_comments: + description: "List footer comments on a page or blog post" + parameters: + parent_type: + description: "Parent content type: page, blogpost" + required: true + parent_id: + description: "Parent content ID" + required: true + cursor: + description: "Pagination cursor from previous response" + limit: + description: "Max results per page (default 25, max 250)" + confluence_create_comment: + description: "Add a footer comment to a page or blog post" + parameters: + parent_type: + description: "Parent content type: page, blogpost" + required: true + parent_id: + description: "Parent content ID" + required: true + body_value: + description: "Comment body content" + required: true + body_format: + description: "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + confluence_delete_comment: + description: "Delete a footer comment by ID" + parameters: + comment_id: + description: "Comment ID" + required: true diff --git a/integrations/datadog/tools.go b/integrations/datadog/tools.go index 1887392e..822cfda2 100644 --- a/integrations/datadog/tools.go +++ b/integrations/datadog/tools.go @@ -1,608 +1,12 @@ package datadog -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Logs ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_search_logs"), Description: "Search Datadog logs for production debugging and observability. Find errors, traces, and events by query string.", - Parameters: map[string]string{"query": "Log search query (e.g., 'service:nginx status:error')", "from": "Start time (ISO 8601, epoch seconds, or relative like 'now-1h')", "to": "End time (default: now)", "limit": "Max results (default 50, max 1000)", "sort": "Sort order: timestamp (asc) or -timestamp (desc, default)"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("datadog_aggregate_logs"), Description: "Aggregate Datadog logs for monitoring analytics (count, sum, avg, etc.). Use for production observability and trend analysis.", - Parameters: map[string]string{"query": "Log search query", "compute_type": "Aggregation type: count, cardinality, avg, sum, min, max, percentile", "compute_field": "Field to aggregate on (required for non-count types, e.g., @duration)", "group_by": "Field to group by (e.g., service, @http.status_code)", "from": "Start time", "to": "End time"}, - Required: []string{"query", "compute_type"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Metrics ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_query_metrics"), Description: "Query Datadog metrics timeseries data for production monitoring. Analyze performance, CPU, memory, latency, and custom metrics.", - Parameters: map[string]string{"query": "Metrics query (e.g., 'avg:system.cpu.user{*}')", "from": "Start time (epoch seconds or relative)", "to": "End time"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("datadog_list_active_metrics"), Description: "List actively reporting metrics from a given time", - Parameters: map[string]string{"from": "Start time (epoch seconds, default: now-1h)", "host": "Filter by host", "tag_filter": "Filter by tag (e.g., env:prod)"}, - }, - { - Name: mcp.ToolName("datadog_search_metrics"), Description: "Search for metrics by name", - Parameters: map[string]string{"query": "Metric name query (e.g., 'system.cpu')"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("datadog_get_metric_metadata"), Description: "Get metadata for a specific metric", - Parameters: map[string]string{"metric": "Metric name (e.g., system.cpu.user)"}, - Required: []string{"metric"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Monitors ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_monitors"), Description: "List Datadog monitors for production alerting and observability. Filter by status, tags, or environment.", - Parameters: map[string]string{"query": "Filter query (e.g., 'status:alert tag:env:prod')", "page": "Page number (0-based)", "page_size": "Results per page (default 100)"}, - }, - { - Name: mcp.ToolName("datadog_search_monitors"), Description: "Search Datadog monitors and alerts by query string. Find production monitoring rules and notification policies.", - Parameters: map[string]string{"query": "Search query (e.g., 'type:metric status:alert')", "page": "Page number", "per_page": "Results per page (default 30)"}, - }, - { - Name: mcp.ToolName("datadog_get_monitor"), Description: "Get a specific monitor by ID", - Parameters: map[string]string{"id": "Monitor ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_monitor"), Description: "Create a new Datadog monitor for production alerting. Set thresholds and notification rules for metrics, logs, or services.", - Parameters: map[string]string{"name": "Monitor name", "type": "Monitor type: metric alert, service check, event alert, query alert, composite, log alert, etc.", "query": "Monitor query", "message": "Notification message (supports @mentions)", "tags": "Comma-separated tags", "priority": "Priority (1-5)"}, - Required: []string{"name", "type", "query"}, - }, - { - Name: mcp.ToolName("datadog_update_monitor"), Description: "Update an existing monitor", - Parameters: map[string]string{"id": "Monitor ID", "name": "New name", "query": "New query", "message": "New message", "tags": "Comma-separated tags", "priority": "Priority (1-5)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_delete_monitor"), Description: "Delete a monitor", - Parameters: map[string]string{"id": "Monitor ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_mute_monitor"), Description: "Mute a monitor (silence notifications)", - Parameters: map[string]string{"id": "Monitor ID", "scope": "Scope to mute (e.g., 'host:myhost')", "end": "End timestamp (epoch seconds, omit for indefinite)"}, - Required: []string{"id"}, - }, - - // ── Dashboards ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_dashboards"), Description: "List all Datadog dashboards for production monitoring and observability visualization", - Parameters: map[string]string{"filter_shared": "Filter shared dashboards (true/false)", "filter_deleted": "Include deleted (true/false)", "count": "Max results (default 100)", "start": "Offset for pagination"}, - }, - { - Name: mcp.ToolName("datadog_get_dashboard"), Description: "Get a specific dashboard by ID", - Parameters: map[string]string{"id": "Dashboard ID (e.g., 'abc-def-ghi')"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_dashboard"), Description: "Create a new dashboard (JSON body)", - Parameters: map[string]string{"title": "Dashboard title", "layout_type": "Layout: ordered or free", "description": "Dashboard description", "widgets_json": "JSON array of widget definitions"}, - Required: []string{"title", "layout_type"}, - }, - { - Name: mcp.ToolName("datadog_delete_dashboard"), Description: "Delete a dashboard", - Parameters: map[string]string{"id": "Dashboard ID"}, - Required: []string{"id"}, - }, - - // ── Events ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_events"), Description: "List Datadog events for production monitoring. Track deployments, changes, alerts, and system events.", - Parameters: map[string]string{"query": "Event search query", "from": "Start time", "to": "End time", "limit": "Max results (default 10)", "sort": "Sort: timestamp (asc) or -timestamp (desc)"}, - }, - { - Name: mcp.ToolName("datadog_search_events"), Description: "Search Datadog events by query. Find production changes, deployments, and system events.", - Parameters: map[string]string{"query": "Event search query", "from": "Start time", "to": "End time", "limit": "Max results (default 10)", "sort": "Sort: timestamp or -timestamp"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("datadog_get_event"), Description: "Get a specific event by ID", - Parameters: map[string]string{"id": "Event ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_event"), Description: "Create a new event", - Parameters: map[string]string{"title": "Event title", "text": "Event text/body", "alert_type": "Type: error, warning, info, success, user_update, recommendation, snapshot", "tags": "Comma-separated tags", "aggregation_key": "Aggregation key for grouping"}, - Required: []string{"title", "text"}, - }, - - // ── Hosts ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_hosts"), Description: "List Datadog hosts (servers and infrastructure). Filter production machines by environment, CPU, load, or tags.", - Parameters: map[string]string{"filter": "Filter string (e.g., 'env:prod')", "sort_field": "Sort by: apps, cpu, iowait, load, etc.", "sort_dir": "Sort direction: asc or desc", "count": "Max results (default 100)", "from": "Seconds since hosts last reported"}, - }, - { - Name: mcp.ToolName("datadog_get_host_totals"), Description: "Get total number of hosts", - Parameters: map[string]string{"from": "Seconds since hosts last reported"}, - }, - { - Name: mcp.ToolName("datadog_mute_host"), Description: "Mute a host", - Parameters: map[string]string{"hostname": "Hostname to mute", "message": "Mute reason", "end": "End timestamp (epoch seconds)", "override": "Override existing mute (true/false)"}, - Required: []string{"hostname"}, - }, - { - Name: mcp.ToolName("datadog_unmute_host"), Description: "Unmute a host", - Parameters: map[string]string{"hostname": "Hostname to unmute"}, - Required: []string{"hostname"}, - }, - - // ── Tags ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_tags"), Description: "List all host tags", - Parameters: map[string]string{"source": "Tag source (e.g., datadog-agent, chef, users)"}, - }, - { - Name: mcp.ToolName("datadog_get_host_tags"), Description: "Get tags for a specific host", - Parameters: map[string]string{"hostname": "Hostname", "source": "Tag source"}, - Required: []string{"hostname"}, - }, - { - Name: mcp.ToolName("datadog_create_host_tags"), Description: "Add tags to a host", - Parameters: map[string]string{"hostname": "Hostname", "tags": "Comma-separated tags (e.g., 'env:prod,role:web')", "source": "Tag source"}, - Required: []string{"hostname", "tags"}, - }, - { - Name: mcp.ToolName("datadog_update_host_tags"), Description: "Replace all tags on a host", - Parameters: map[string]string{"hostname": "Hostname", "tags": "Comma-separated tags", "source": "Tag source"}, - Required: []string{"hostname", "tags"}, - }, - { - Name: mcp.ToolName("datadog_delete_host_tags"), Description: "Remove tags from a host", - Parameters: map[string]string{"hostname": "Hostname", "source": "Tag source"}, - Required: []string{"hostname"}, - }, - - // ── SLOs ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_slos"), Description: "List Service Level Objectives", - Parameters: map[string]string{"ids": "Comma-separated SLO IDs", "query": "Filter query (e.g., 'name:my-slo')", "tags_query": "Filter by tags (e.g., 'env:prod')", "limit": "Max results (default 1000)", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("datadog_search_slos"), Description: "Search SLOs", - Parameters: map[string]string{"query": "Search query", "page_size": "Results per page", "page_number": "Page number"}, - }, - { - Name: mcp.ToolName("datadog_get_slo"), Description: "Get a specific SLO by ID", - Parameters: map[string]string{"id": "SLO ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_get_slo_history"), Description: "Get SLO history data", - Parameters: map[string]string{"id": "SLO ID", "from": "Start time (epoch seconds)", "to": "End time (epoch seconds)"}, - Required: []string{"id", "from", "to"}, - }, - { - Name: mcp.ToolName("datadog_create_slo"), Description: "Create a new SLO", - Parameters: map[string]string{"name": "SLO name", "type": "Type: metric or monitor", "description": "Description", "target": "Target percentage (e.g., 99.9)", "timeframe": "Timeframe: 7d, 30d, 90d", "monitor_ids": "Comma-separated monitor IDs (for monitor type)", "query_numerator": "Good events query (for metric type)", "query_denominator": "Total events query (for metric type)", "tags": "Comma-separated tags"}, - Required: []string{"name", "type", "target", "timeframe"}, - }, - { - Name: mcp.ToolName("datadog_delete_slo"), Description: "Delete a SLO", - Parameters: map[string]string{"id": "SLO ID"}, - Required: []string{"id"}, - }, - - // ── Downtimes ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_downtimes"), Description: "List scheduled downtimes", - Parameters: map[string]string{"current_only": "Only show current downtimes (true/false)"}, - }, - { - Name: mcp.ToolName("datadog_get_downtime"), Description: "Get a specific downtime by ID", - Parameters: map[string]string{"id": "Downtime ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_downtime"), Description: "Schedule a downtime", - Parameters: map[string]string{"scope": "Scope (e.g., 'env:prod', 'host:myhost')", "message": "Message/reason", "start": "Start time (epoch seconds, default: now)", "end": "End time (epoch seconds)", "monitor_identifier_type": "Type: id or tags", "monitor_identifier_id": "Monitor ID (when type=id)", "monitor_identifier_tags": "Monitor tags (when type=tags, comma-separated)"}, - Required: []string{"scope"}, - }, - { - Name: mcp.ToolName("datadog_cancel_downtime"), Description: "Cancel a scheduled downtime", - Parameters: map[string]string{"id": "Downtime ID"}, - Required: []string{"id"}, - }, - - // ── Incidents ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_incidents"), Description: "List Datadog incidents for production outages and service disruptions. Track severity and incident response.", - Parameters: map[string]string{"page_size": "Results per page (default 10)", "page_offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("datadog_search_incidents"), Description: "Search Datadog incidents by query. Find production outages, disruptions, and postmortems by severity, status, or team.", - Parameters: map[string]string{"query": "Incident search query (e.g., 'state:active AND severity:SEV-1')", "sort": "Sort order: created or -created (default)", "page_size": "Results per page (default 10)", "page_offset": "Pagination offset"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("datadog_get_incident"), Description: "Get details of a specific Datadog incident, including timeline and response data for outage investigation", - Parameters: map[string]string{"id": "Incident ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_incident"), Description: "Create a new incident", - Parameters: map[string]string{"title": "Incident title", "severity": "Severity: SEV-1, SEV-2, SEV-3, SEV-4, SEV-5, UNKNOWN", "customer_impacted": "Customer impacted (true/false)"}, - Required: []string{"title", "customer_impacted"}, - }, - { - Name: mcp.ToolName("datadog_update_incident"), Description: "Update an incident", - Parameters: map[string]string{"id": "Incident ID", "title": "New title", "severity": "New severity", "status": "Status: active, stable, resolved", "customer_impacted": "Customer impacted (true/false)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_list_incident_attachments"), Description: "List attachments for a Datadog incident. View postmortems and linked resources attached to an outage.", - Parameters: map[string]string{"incident_id": "Incident ID"}, - Required: []string{"incident_id"}, - }, - { - Name: mcp.ToolName("datadog_list_incident_todos"), Description: "List todos and action items for a Datadog incident. Track remediation tasks and follow-up work.", - Parameters: map[string]string{"incident_id": "Incident ID"}, - Required: []string{"incident_id"}, - }, - - // ── Incident Services ──────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_incident_services"), Description: "List Datadog incident services. View services configured for incident management and response.", - Parameters: map[string]string{"page_size": "Results per page (default 10)", "page_offset": "Pagination offset", "filter": "Filter services by name"}, - }, - { - Name: mcp.ToolName("datadog_get_incident_service"), Description: "Get details of a specific incident service. Use after list_incident_services.", - Parameters: map[string]string{"service_id": "Incident service ID"}, - Required: []string{"service_id"}, - }, - { - Name: mcp.ToolName("datadog_create_incident_service"), Description: "Create a new incident service for incident management", - Parameters: map[string]string{"name": "Service name"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("datadog_update_incident_service"), Description: "Update an existing incident service", - Parameters: map[string]string{"service_id": "Incident service ID", "name": "New service name"}, - Required: []string{"service_id", "name"}, - }, - { - Name: mcp.ToolName("datadog_delete_incident_service"), Description: "Delete an incident service", - Parameters: map[string]string{"service_id": "Incident service ID"}, - Required: []string{"service_id"}, - }, - - // ── Incident Teams ─────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_incident_teams"), Description: "List Datadog incident teams. View teams configured for incident response and on-call rotation.", - Parameters: map[string]string{"page_size": "Results per page (default 10)", "page_offset": "Pagination offset", "filter": "Filter teams by name"}, - }, - { - Name: mcp.ToolName("datadog_get_incident_team"), Description: "Get details of a specific incident team. Use after list_incident_teams.", - Parameters: map[string]string{"team_id": "Incident team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_create_incident_team"), Description: "Create a new incident team for incident response", - Parameters: map[string]string{"name": "Team name"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("datadog_update_incident_team"), Description: "Update an existing incident team", - Parameters: map[string]string{"team_id": "Incident team ID", "name": "New team name"}, - Required: []string{"team_id", "name"}, - }, - { - Name: mcp.ToolName("datadog_delete_incident_team"), Description: "Delete an incident team", - Parameters: map[string]string{"team_id": "Incident team ID"}, - Required: []string{"team_id"}, - }, - - // ── Synthetics ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_synthetics_tests"), Description: "List synthetic monitoring tests", - Parameters: map[string]string{"page_size": "Results per page (default 100)", "page_number": "Page number"}, - }, - { - Name: mcp.ToolName("datadog_get_synthetics_api_test"), Description: "Get a specific synthetics API test", - Parameters: map[string]string{"id": "Test public ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_get_synthetics_test_result"), Description: "Get latest results for a synthetics test", - Parameters: map[string]string{"id": "Test public ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_trigger_synthetics_tests"), Description: "Trigger synthetic tests on demand", - Parameters: map[string]string{"public_ids": "Comma-separated test public IDs to trigger"}, - Required: []string{"public_ids"}, - }, - - // ── Notebooks ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_notebooks"), Description: "List Datadog notebooks", - Parameters: map[string]string{"query": "Search query", "count": "Max results (default 100)", "start": "Offset for pagination", "sort_field": "Sort by: modified or name", "sort_dir": "Sort direction: asc or desc"}, - }, - { - Name: mcp.ToolName("datadog_get_notebook"), Description: "Get a specific notebook by ID", - Parameters: map[string]string{"id": "Notebook ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("datadog_create_notebook"), Description: "Create a new notebook (JSON body)", - Parameters: map[string]string{"name": "Notebook name", "cells_json": "JSON array of notebook cells", "time_from": "Time range start", "time_to": "Time range end"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("datadog_delete_notebook"), Description: "Delete a notebook", - Parameters: map[string]string{"id": "Notebook ID"}, - Required: []string{"id"}, - }, - - // ── Users ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_users"), Description: "List users in the organization", - Parameters: map[string]string{"page_size": "Results per page (default 10)", "page_number": "Page number", "sort": "Sort field (e.g., name, email)", "filter": "Filter string"}, - }, - { - Name: mcp.ToolName("datadog_get_user"), Description: "Get a specific user by ID", - Parameters: map[string]string{"id": "User ID"}, - Required: []string{"id"}, - }, - - // ── Teams ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_teams"), Description: "List Datadog teams. View organizational teams, ownership, and membership. Start here for team management.", - Parameters: map[string]string{"page_size": "Results per page (default 10)", "page_number": "Page number", "filter": "Filter teams by keyword/name", "sort": "Sort: name, -name, user_count, -user_count"}, - }, - { - Name: mcp.ToolName("datadog_get_team"), Description: "Get details of a specific Datadog team including handle, description, and member count. Use after list_teams.", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_create_team"), Description: "Create a new Datadog team", - Parameters: map[string]string{"name": "Team name", "handle": "Team handle (URL-safe identifier)", "description": "Team description"}, - Required: []string{"name", "handle"}, - }, - { - Name: mcp.ToolName("datadog_update_team"), Description: "Update an existing team", - Parameters: map[string]string{"team_id": "Team ID", "name": "New team name", "handle": "New team handle", "description": "New description"}, - Required: []string{"team_id", "name", "handle"}, - }, - { - Name: mcp.ToolName("datadog_delete_team"), Description: "Delete a team", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_list_team_members"), Description: "List members of a Datadog team. View who belongs to a team and their roles.", - Parameters: map[string]string{"team_id": "Team ID", "page_size": "Results per page (default 10)", "page_number": "Page number", "filter": "Filter members by keyword"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_add_team_member"), Description: "Add a user to a team", - Parameters: map[string]string{"team_id": "Team ID", "user_id": "User ID to add", "role": "Role: admin (omit for regular member)"}, - Required: []string{"team_id", "user_id"}, - }, - { - Name: mcp.ToolName("datadog_update_team_member"), Description: "Update a team member's role", - Parameters: map[string]string{"team_id": "Team ID", "user_id": "User ID", "role": "New role: admin (omit for regular member)"}, - Required: []string{"team_id", "user_id"}, - }, - { - Name: mcp.ToolName("datadog_remove_team_member"), Description: "Remove a user from a team", - Parameters: map[string]string{"team_id": "Team ID", "user_id": "User ID to remove"}, - Required: []string{"team_id", "user_id"}, - }, - { - Name: mcp.ToolName("datadog_get_user_team_memberships"), Description: "Get all team memberships for a user. Find which teams a user belongs to.", - Parameters: map[string]string{"user_id": "User ID"}, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("datadog_list_team_links"), Description: "List external links for a team. View runbooks, dashboards, and documentation links associated with a team.", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_get_team_link"), Description: "Get a specific team link. Use after list_team_links.", - Parameters: map[string]string{"team_id": "Team ID", "link_id": "Link ID"}, - Required: []string{"team_id", "link_id"}, - }, - { - Name: mcp.ToolName("datadog_create_team_link"), Description: "Add a link to a team (runbook, dashboard, documentation)", - Parameters: map[string]string{"team_id": "Team ID", "label": "Link label", "url": "Link URL"}, - Required: []string{"team_id", "label", "url"}, - }, - { - Name: mcp.ToolName("datadog_update_team_link"), Description: "Update a team link", - Parameters: map[string]string{"team_id": "Team ID", "link_id": "Link ID", "label": "New label", "url": "New URL"}, - Required: []string{"team_id", "link_id", "label", "url"}, - }, - { - Name: mcp.ToolName("datadog_delete_team_link"), Description: "Delete a team link", - Parameters: map[string]string{"team_id": "Team ID", "link_id": "Link ID"}, - Required: []string{"team_id", "link_id"}, - }, - { - Name: mcp.ToolName("datadog_get_team_permission_settings"), Description: "Get permission settings for a team. View who can manage membership and edit the team.", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_update_team_permission_setting"), Description: "Update a team permission setting", - Parameters: map[string]string{"team_id": "Team ID", "action": "Permission action: manage_membership or edit", "value": "Permission value: admins, members, organization, user_access_manage, teams_manage"}, - Required: []string{"team_id", "action", "value"}, - }, - - // ── Spans / APM ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_search_spans"), Description: "Search Datadog APM spans and distributed traces for production performance debugging. Investigate latency and service dependencies.", - Parameters: map[string]string{"query": "Span search query (e.g., 'service:web-store resource_name:GET')", "from": "Start time", "to": "End time", "limit": "Max results (default 10)", "sort": "Sort: timestamp or -timestamp"}, - Required: []string{"query"}, - }, - - // ── Software Catalog ────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_services"), Description: "List services from the Datadog Software Catalog. View production microservices, dependencies, and ownership.", - Parameters: map[string]string{"page_size": "Results per page (default 20)", "page_offset": "Pagination offset"}, - }, - - // ── On-Call ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_oncall_schedules"), Description: "List all Datadog On-Call schedules. Start here to find schedule IDs before getting details.", - Parameters: map[string]string{"include": "Comma-separated related resources to include (e.g. teams)"}, - }, - { - Name: mcp.ToolName("datadog_get_oncall_schedule"), Description: "Get a Datadog On-Call schedule with layers, members, and rotation details. View who is on-call and when.", - Parameters: map[string]string{"schedule_id": "On-Call schedule ID"}, - Required: []string{"schedule_id"}, - }, - { - Name: mcp.ToolName("datadog_create_oncall_schedule"), Description: "Create a new On-Call schedule with layers, rotation intervals, and members. Use body_json with Datadog schedule create schema.", - Parameters: map[string]string{"body_json": "JSON body matching Datadog ScheduleCreateRequest schema: {\"data\":{\"type\":\"schedules\",\"attributes\":{\"name\":\"...\",\"time_zone\":\"...\",\"layers\":[{\"name\":\"...\",\"effective_date\":\"...\",\"rotation_start\":\"...\",\"interval\":{\"days\":7},\"members\":[{\"user\":{\"id\":\"...\"}}]}]}}}"}, - Required: []string{"body_json"}, - }, - { - Name: mcp.ToolName("datadog_update_oncall_schedule"), Description: "Update an existing On-Call schedule (layers, members, rotations). IMPORTANT: Always include the full relationships.teams block in body_json — omitting it silently removes the team association. Fetch current schedule with get_oncall_schedule first, then modify and submit.", - Parameters: map[string]string{"schedule_id": "Schedule ID", "body_json": "JSON body matching Datadog ScheduleUpdateRequest schema. MUST include relationships.teams to preserve team association."}, - Required: []string{"schedule_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_delete_oncall_schedule"), Description: "Delete an On-Call schedule", - Parameters: map[string]string{"schedule_id": "Schedule ID"}, - Required: []string{"schedule_id"}, - }, - { - Name: mcp.ToolName("datadog_get_schedule_oncall_user"), Description: "Get the current on-call user for a schedule. Find who is on-call right now for a given rotation.", - Parameters: map[string]string{"schedule_id": "On-Call schedule ID"}, - Required: []string{"schedule_id"}, - }, - { - Name: mcp.ToolName("datadog_list_oncall_escalation_policies"), Description: "List all Datadog On-Call escalation policies. Start here to find policy IDs before getting details.", - Parameters: map[string]string{"include": "Comma-separated related resources to include (e.g. teams,steps,steps.targets)"}, - }, - { - Name: mcp.ToolName("datadog_get_oncall_escalation_policy"), Description: "Get a Datadog On-Call escalation policy with steps, targets, and notification chain. View escalation rules and timing.", - Parameters: map[string]string{"policy_id": "Escalation policy ID"}, - Required: []string{"policy_id"}, - }, - { - Name: mcp.ToolName("datadog_create_oncall_escalation_policy"), Description: "Create a new On-Call escalation policy with steps and targets. Use body_json with Datadog escalation policy create schema.", - Parameters: map[string]string{"body_json": "JSON body matching Datadog EscalationPolicyCreateRequest schema: {\"data\":{\"type\":\"policies\",\"attributes\":{\"name\":\"...\",\"steps\":[{\"targets\":[{\"type\":\"users\",\"id\":\"...\"}],\"escalate_after_seconds\":300}]}}}"}, - Required: []string{"body_json"}, - }, - { - Name: mcp.ToolName("datadog_update_oncall_escalation_policy"), Description: "Update an existing On-Call escalation policy (steps, targets, timing). Fetch current policy with get_oncall_escalation_policy first, then modify and submit.", - Parameters: map[string]string{"policy_id": "Escalation policy ID", "body_json": "JSON body matching Datadog EscalationPolicyUpdateRequest schema"}, - Required: []string{"policy_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_delete_oncall_escalation_policy"), Description: "Delete an On-Call escalation policy", - Parameters: map[string]string{"policy_id": "Escalation policy ID"}, - Required: []string{"policy_id"}, - }, - { - Name: mcp.ToolName("datadog_get_oncall_team_routing_rules"), Description: "Get On-Call routing rules for a team. View how pages are routed to schedules and escalation policies.", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - { - Name: mcp.ToolName("datadog_set_oncall_team_routing_rules"), Description: "Set (replace) On-Call routing rules for a team. Define how pages are routed to escalation policies. Fetch current rules with get_oncall_team_routing_rules first.", - Parameters: map[string]string{"team_id": "Team ID", "body_json": "JSON body matching Datadog TeamRoutingRulesRequest schema: {\"data\":{\"type\":\"team_routing_rules\",\"attributes\":{\"rules\":[{\"policy_id\":\"...\",\"urgency\":\"high\"}]}}}"}, - Required: []string{"team_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_get_team_oncall_users"), Description: "Get the current on-call users for a team. Find who is on-call right now across all team schedules.", - Parameters: map[string]string{"team_id": "Team ID"}, - Required: []string{"team_id"}, - }, - - // ── On-Call Paging ─────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_oncall_pages"), Description: "List On-Call pages. Filter by status and urgency to find active or past pages.", - Parameters: map[string]string{"include": "Comma-separated related resources to include", "status": "Filter by page status (e.g. triggered, acknowledged, resolved)", "urgency": "Filter by urgency (low or high)"}, - }, - { - Name: mcp.ToolName("datadog_get_oncall_page"), Description: "Get details of a specific On-Call page including status, responders, and timeline.", - Parameters: map[string]string{"page_id": "Page UUID", "include": "Comma-separated related resources to include"}, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("datadog_create_oncall_page"), Description: "Create a new On-Call page to alert responders. Page a team or user for production incidents and urgent issues.", - Parameters: map[string]string{"title": "Page title", "urgency": "Urgency: low or high (default high)", "target_id": "Target identifier (team handle, team ID, or user ID)", "target_type": "Target type: team_handle, team_id, or user_id (default team_handle)", "description": "Page description with details", "tags": "Comma-separated tags"}, - Required: []string{"title", "target_id"}, - }, - { - Name: mcp.ToolName("datadog_acknowledge_oncall_page"), Description: "Acknowledge an On-Call page to indicate responder awareness", - Parameters: map[string]string{"page_id": "Page UUID"}, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("datadog_escalate_oncall_page"), Description: "Escalate an On-Call page to the next responder in the escalation policy", - Parameters: map[string]string{"page_id": "Page UUID"}, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("datadog_resolve_oncall_page"), Description: "Resolve an On-Call page to mark the issue as handled", - Parameters: map[string]string{"page_id": "Page UUID"}, - Required: []string{"page_id"}, - }, - - // ── Notification Channels ─────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_user_notification_channels"), Description: "List On-Call notification channels for a user (email, Slack, SMS, push). View how a user receives on-call alerts.", - Parameters: map[string]string{"user_id": "User ID"}, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("datadog_create_user_notification_channel"), Description: "Create a notification channel for a user (email, Slack, SMS, push).", - Parameters: map[string]string{"user_id": "User ID", "body_json": "JSON body matching Datadog CreateUserNotificationChannelRequest schema"}, - Required: []string{"user_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_get_user_notification_channel"), Description: "Get a specific notification channel for a user.", - Parameters: map[string]string{"user_id": "User ID", "channel_id": "Notification channel ID"}, - Required: []string{"user_id", "channel_id"}, - }, - { - Name: mcp.ToolName("datadog_delete_user_notification_channel"), Description: "Delete a notification channel for a user.", - Parameters: map[string]string{"user_id": "User ID", "channel_id": "Notification channel ID"}, - Required: []string{"user_id", "channel_id"}, - }, - - // ── Notification Rules ───────────────────────────────────────── - { - Name: mcp.ToolName("datadog_list_user_notification_rules"), Description: "List On-Call notification rules for a user. Rules define when and how a user is notified for on-call pages.", - Parameters: map[string]string{"user_id": "User ID", "include": "Comma-separated related resources to include"}, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("datadog_create_user_notification_rule"), Description: "Create a notification rule for a user defining when and how they are notified for on-call pages.", - Parameters: map[string]string{"user_id": "User ID", "body_json": "JSON body matching Datadog CreateOnCallNotificationRuleRequest schema"}, - Required: []string{"user_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_get_user_notification_rule"), Description: "Get a specific notification rule for a user.", - Parameters: map[string]string{"user_id": "User ID", "rule_id": "Notification rule ID", "include": "Comma-separated related resources to include"}, - Required: []string{"user_id", "rule_id"}, - }, - { - Name: mcp.ToolName("datadog_update_user_notification_rule"), Description: "Update a notification rule for a user.", - Parameters: map[string]string{"user_id": "User ID", "rule_id": "Notification rule ID", "body_json": "JSON body matching Datadog UpdateOnCallNotificationRuleRequest schema", "include": "Comma-separated related resources to include"}, - Required: []string{"user_id", "rule_id", "body_json"}, - }, - { - Name: mcp.ToolName("datadog_delete_user_notification_rule"), Description: "Delete a notification rule for a user.", - Parameters: map[string]string{"user_id": "User ID", "rule_id": "Notification rule ID"}, - Required: []string{"user_id", "rule_id"}, - }, - - // ── IP Ranges ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("datadog_get_ip_ranges"), Description: "Get Datadog IP address ranges used for inbound/outbound traffic", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/datadog/tools.yaml b/integrations/datadog/tools.yaml new file mode 100644 index 00000000..02ac4aee --- /dev/null +++ b/integrations/datadog/tools.yaml @@ -0,0 +1,1037 @@ +version: 1 +tools: + datadog_search_logs: + description: "Search Datadog logs for production debugging and observability. Find errors, traces, and events by query string." + parameters: + query: + description: "Log search query (e.g., 'service:nginx status:error')" + required: true + from: + description: "Start time (ISO 8601, epoch seconds, or relative like 'now-1h')" + to: + description: "End time (default: now)" + limit: + description: "Max results (default 50, max 1000)" + sort: + description: "Sort order: timestamp (asc) or -timestamp (desc, default)" + datadog_aggregate_logs: + description: "Aggregate Datadog logs for monitoring analytics (count, sum, avg, etc.). Use for production observability and trend analysis." + parameters: + query: + description: "Log search query" + required: true + compute_type: + description: "Aggregation type: count, cardinality, avg, sum, min, max, percentile" + required: true + compute_field: + description: "Field to aggregate on (required for non-count types, e.g., @duration)" + group_by: + description: "Field to group by (e.g., service, @http.status_code)" + from: + description: "Start time" + to: + description: "End time" + datadog_query_metrics: + description: "Query Datadog metrics timeseries data for production monitoring. Analyze performance, CPU, memory, latency, and custom metrics." + parameters: + query: + description: "Metrics query (e.g., 'avg:system.cpu.user{*}')" + required: true + from: + description: "Start time (epoch seconds or relative)" + to: + description: "End time" + datadog_list_active_metrics: + description: "List actively reporting metrics from a given time" + parameters: + from: + description: "Start time (epoch seconds, default: now-1h)" + host: + description: "Filter by host" + tag_filter: + description: "Filter by tag (e.g., env:prod)" + datadog_search_metrics: + description: "Search for metrics by name" + parameters: + query: + description: "Metric name query (e.g., 'system.cpu')" + required: true + datadog_get_metric_metadata: + description: "Get metadata for a specific metric" + parameters: + metric: + description: "Metric name (e.g., system.cpu.user)" + required: true + datadog_list_monitors: + description: "List Datadog monitors for production alerting and observability. Filter by status, tags, or environment." + parameters: + query: + description: "Filter query (e.g., 'status:alert tag:env:prod')" + page: + description: "Page number (0-based)" + page_size: + description: "Results per page (default 100)" + datadog_search_monitors: + description: "Search Datadog monitors and alerts by query string. Find production monitoring rules and notification policies." + parameters: + query: + description: "Search query (e.g., 'type:metric status:alert')" + page: + description: "Page number" + per_page: + description: "Results per page (default 30)" + datadog_get_monitor: + description: "Get a specific monitor by ID" + parameters: + id: + description: "Monitor ID" + required: true + datadog_create_monitor: + description: "Create a new Datadog monitor for production alerting. Set thresholds and notification rules for metrics, logs, or services." + parameters: + name: + description: "Monitor name" + required: true + type: + description: "Monitor type: metric alert, service check, event alert, query alert, composite, log alert, etc." + required: true + query: + description: "Monitor query" + required: true + message: + description: "Notification message (supports @mentions)" + tags: + description: "Comma-separated tags" + priority: + description: "Priority (1-5)" + datadog_update_monitor: + description: "Update an existing monitor" + parameters: + id: + description: "Monitor ID" + required: true + name: + description: "New name" + query: + description: "New query" + message: + description: "New message" + tags: + description: "Comma-separated tags" + priority: + description: "Priority (1-5)" + datadog_delete_monitor: + description: "Delete a monitor" + parameters: + id: + description: "Monitor ID" + required: true + datadog_mute_monitor: + description: "Mute a monitor (silence notifications)" + parameters: + id: + description: "Monitor ID" + required: true + scope: + description: "Scope to mute (e.g., 'host:myhost')" + end: + description: "End timestamp (epoch seconds, omit for indefinite)" + datadog_list_dashboards: + description: "List all Datadog dashboards for production monitoring and observability visualization" + parameters: + filter_shared: + description: "Filter shared dashboards (true/false)" + filter_deleted: + description: "Include deleted (true/false)" + count: + description: "Max results (default 100)" + start: + description: "Offset for pagination" + datadog_get_dashboard: + description: "Get a specific dashboard by ID" + parameters: + id: + description: "Dashboard ID (e.g., 'abc-def-ghi')" + required: true + datadog_create_dashboard: + description: "Create a new dashboard (JSON body)" + parameters: + title: + description: "Dashboard title" + required: true + layout_type: + description: "Layout: ordered or free" + required: true + description: + description: "Dashboard description" + widgets_json: + description: "JSON array of widget definitions" + datadog_delete_dashboard: + description: "Delete a dashboard" + parameters: + id: + description: "Dashboard ID" + required: true + datadog_list_events: + description: "List Datadog events for production monitoring. Track deployments, changes, alerts, and system events." + parameters: + query: + description: "Event search query" + from: + description: "Start time" + to: + description: "End time" + limit: + description: "Max results (default 10)" + sort: + description: "Sort: timestamp (asc) or -timestamp (desc)" + datadog_search_events: + description: "Search Datadog events by query. Find production changes, deployments, and system events." + parameters: + query: + description: "Event search query" + required: true + from: + description: "Start time" + to: + description: "End time" + limit: + description: "Max results (default 10)" + sort: + description: "Sort: timestamp or -timestamp" + datadog_get_event: + description: "Get a specific event by ID" + parameters: + id: + description: "Event ID" + required: true + datadog_create_event: + description: "Create a new event" + parameters: + title: + description: "Event title" + required: true + text: + description: "Event text/body" + required: true + alert_type: + description: "Type: error, warning, info, success, user_update, recommendation, snapshot" + tags: + description: "Comma-separated tags" + aggregation_key: + description: "Aggregation key for grouping" + datadog_list_hosts: + description: "List Datadog hosts (servers and infrastructure). Filter production machines by environment, CPU, load, or tags." + parameters: + filter: + description: "Filter string (e.g., 'env:prod')" + sort_field: + description: "Sort by: apps, cpu, iowait, load, etc." + sort_dir: + description: "Sort direction: asc or desc" + count: + description: "Max results (default 100)" + from: + description: "Seconds since hosts last reported" + datadog_get_host_totals: + description: "Get total number of hosts" + parameters: + from: + description: "Seconds since hosts last reported" + datadog_mute_host: + description: "Mute a host" + parameters: + hostname: + description: "Hostname to mute" + required: true + message: + description: "Mute reason" + end: + description: "End timestamp (epoch seconds)" + override: + description: "Override existing mute (true/false)" + datadog_unmute_host: + description: "Unmute a host" + parameters: + hostname: + description: "Hostname to unmute" + required: true + datadog_list_tags: + description: "List all host tags" + parameters: + source: + description: "Tag source (e.g., datadog-agent, chef, users)" + datadog_get_host_tags: + description: "Get tags for a specific host" + parameters: + hostname: + description: "Hostname" + required: true + source: + description: "Tag source" + datadog_create_host_tags: + description: "Add tags to a host" + parameters: + hostname: + description: "Hostname" + required: true + tags: + description: "Comma-separated tags (e.g., 'env:prod,role:web')" + required: true + source: + description: "Tag source" + datadog_update_host_tags: + description: "Replace all tags on a host" + parameters: + hostname: + description: "Hostname" + required: true + tags: + description: "Comma-separated tags" + required: true + source: + description: "Tag source" + datadog_delete_host_tags: + description: "Remove tags from a host" + parameters: + hostname: + description: "Hostname" + required: true + source: + description: "Tag source" + datadog_list_slos: + description: "List Service Level Objectives" + parameters: + ids: + description: "Comma-separated SLO IDs" + query: + description: "Filter query (e.g., 'name:my-slo')" + tags_query: + description: "Filter by tags (e.g., 'env:prod')" + limit: + description: "Max results (default 1000)" + offset: + description: "Pagination offset" + datadog_search_slos: + description: "Search SLOs" + parameters: + query: + description: "Search query" + page_size: + description: "Results per page" + page_number: + description: "Page number" + datadog_get_slo: + description: "Get a specific SLO by ID" + parameters: + id: + description: "SLO ID" + required: true + datadog_get_slo_history: + description: "Get SLO history data" + parameters: + id: + description: "SLO ID" + required: true + from: + description: "Start time (epoch seconds)" + required: true + to: + description: "End time (epoch seconds)" + required: true + datadog_create_slo: + description: "Create a new SLO" + parameters: + name: + description: "SLO name" + required: true + type: + description: "Type: metric or monitor" + required: true + description: + description: "Description" + target: + description: "Target percentage (e.g., 99.9)" + required: true + timeframe: + description: "Timeframe: 7d, 30d, 90d" + required: true + monitor_ids: + description: "Comma-separated monitor IDs (for monitor type)" + query_numerator: + description: "Good events query (for metric type)" + query_denominator: + description: "Total events query (for metric type)" + tags: + description: "Comma-separated tags" + datadog_delete_slo: + description: "Delete a SLO" + parameters: + id: + description: "SLO ID" + required: true + datadog_list_downtimes: + description: "List scheduled downtimes" + parameters: + current_only: + description: "Only show current downtimes (true/false)" + datadog_get_downtime: + description: "Get a specific downtime by ID" + parameters: + id: + description: "Downtime ID" + required: true + datadog_create_downtime: + description: "Schedule a downtime" + parameters: + scope: + description: "Scope (e.g., 'env:prod', 'host:myhost')" + required: true + message: + description: "Message/reason" + start: + description: "Start time (epoch seconds, default: now)" + end: + description: "End time (epoch seconds)" + monitor_identifier_type: + description: "Type: id or tags" + monitor_identifier_id: + description: "Monitor ID (when type=id)" + monitor_identifier_tags: + description: "Monitor tags (when type=tags, comma-separated)" + datadog_cancel_downtime: + description: "Cancel a scheduled downtime" + parameters: + id: + description: "Downtime ID" + required: true + datadog_list_incidents: + description: "List Datadog incidents for production outages and service disruptions. Track severity and incident response." + parameters: + page_size: + description: "Results per page (default 10)" + page_offset: + description: "Pagination offset" + datadog_search_incidents: + description: "Search Datadog incidents by query. Find production outages, disruptions, and postmortems by severity, status, or team." + parameters: + query: + description: "Incident search query (e.g., 'state:active AND severity:SEV-1')" + required: true + sort: + description: "Sort order: created or -created (default)" + page_size: + description: "Results per page (default 10)" + page_offset: + description: "Pagination offset" + datadog_get_incident: + description: "Get details of a specific Datadog incident, including timeline and response data for outage investigation" + parameters: + id: + description: "Incident ID" + required: true + datadog_create_incident: + description: "Create a new incident" + parameters: + title: + description: "Incident title" + required: true + severity: + description: "Severity: SEV-1, SEV-2, SEV-3, SEV-4, SEV-5, UNKNOWN" + customer_impacted: + description: "Customer impacted (true/false)" + required: true + datadog_update_incident: + description: "Update an incident" + parameters: + id: + description: "Incident ID" + required: true + title: + description: "New title" + severity: + description: "New severity" + status: + description: "Status: active, stable, resolved" + customer_impacted: + description: "Customer impacted (true/false)" + datadog_list_incident_attachments: + description: "List attachments for a Datadog incident. View postmortems and linked resources attached to an outage." + parameters: + incident_id: + description: "Incident ID" + required: true + datadog_list_incident_todos: + description: "List todos and action items for a Datadog incident. Track remediation tasks and follow-up work." + parameters: + incident_id: + description: "Incident ID" + required: true + datadog_list_incident_services: + description: "List Datadog incident services. View services configured for incident management and response." + parameters: + page_size: + description: "Results per page (default 10)" + page_offset: + description: "Pagination offset" + filter: + description: "Filter services by name" + datadog_get_incident_service: + description: "Get details of a specific incident service. Use after list_incident_services." + parameters: + service_id: + description: "Incident service ID" + required: true + datadog_create_incident_service: + description: "Create a new incident service for incident management" + parameters: + name: + description: "Service name" + required: true + datadog_update_incident_service: + description: "Update an existing incident service" + parameters: + service_id: + description: "Incident service ID" + required: true + name: + description: "New service name" + required: true + datadog_delete_incident_service: + description: "Delete an incident service" + parameters: + service_id: + description: "Incident service ID" + required: true + datadog_list_incident_teams: + description: "List Datadog incident teams. View teams configured for incident response and on-call rotation." + parameters: + page_size: + description: "Results per page (default 10)" + page_offset: + description: "Pagination offset" + filter: + description: "Filter teams by name" + datadog_get_incident_team: + description: "Get details of a specific incident team. Use after list_incident_teams." + parameters: + team_id: + description: "Incident team ID" + required: true + datadog_create_incident_team: + description: "Create a new incident team for incident response" + parameters: + name: + description: "Team name" + required: true + datadog_update_incident_team: + description: "Update an existing incident team" + parameters: + team_id: + description: "Incident team ID" + required: true + name: + description: "New team name" + required: true + datadog_delete_incident_team: + description: "Delete an incident team" + parameters: + team_id: + description: "Incident team ID" + required: true + datadog_list_synthetics_tests: + description: "List synthetic monitoring tests" + parameters: + page_size: + description: "Results per page (default 100)" + page_number: + description: "Page number" + datadog_get_synthetics_api_test: + description: "Get a specific synthetics API test" + parameters: + id: + description: "Test public ID" + required: true + datadog_get_synthetics_test_result: + description: "Get latest results for a synthetics test" + parameters: + id: + description: "Test public ID" + required: true + datadog_trigger_synthetics_tests: + description: "Trigger synthetic tests on demand" + parameters: + public_ids: + description: "Comma-separated test public IDs to trigger" + required: true + datadog_list_notebooks: + description: "List Datadog notebooks" + parameters: + query: + description: "Search query" + count: + description: "Max results (default 100)" + start: + description: "Offset for pagination" + sort_field: + description: "Sort by: modified or name" + sort_dir: + description: "Sort direction: asc or desc" + datadog_get_notebook: + description: "Get a specific notebook by ID" + parameters: + id: + description: "Notebook ID" + required: true + datadog_create_notebook: + description: "Create a new notebook (JSON body)" + parameters: + name: + description: "Notebook name" + required: true + cells_json: + description: "JSON array of notebook cells" + time_from: + description: "Time range start" + time_to: + description: "Time range end" + datadog_delete_notebook: + description: "Delete a notebook" + parameters: + id: + description: "Notebook ID" + required: true + datadog_list_users: + description: "List users in the organization" + parameters: + page_size: + description: "Results per page (default 10)" + page_number: + description: "Page number" + sort: + description: "Sort field (e.g., name, email)" + filter: + description: "Filter string" + datadog_get_user: + description: "Get a specific user by ID" + parameters: + id: + description: "User ID" + required: true + datadog_list_teams: + description: "List Datadog teams. View organizational teams, ownership, and membership. Start here for team management." + parameters: + page_size: + description: "Results per page (default 10)" + page_number: + description: "Page number" + filter: + description: "Filter teams by keyword/name" + sort: + description: "Sort: name, -name, user_count, -user_count" + datadog_get_team: + description: "Get details of a specific Datadog team including handle, description, and member count. Use after list_teams." + parameters: + team_id: + description: "Team ID" + required: true + datadog_create_team: + description: "Create a new Datadog team" + parameters: + name: + description: "Team name" + required: true + handle: + description: "Team handle (URL-safe identifier)" + required: true + description: + description: "Team description" + datadog_update_team: + description: "Update an existing team" + parameters: + team_id: + description: "Team ID" + required: true + name: + description: "New team name" + required: true + handle: + description: "New team handle" + required: true + description: + description: "New description" + datadog_delete_team: + description: "Delete a team" + parameters: + team_id: + description: "Team ID" + required: true + datadog_list_team_members: + description: "List members of a Datadog team. View who belongs to a team and their roles." + parameters: + team_id: + description: "Team ID" + required: true + page_size: + description: "Results per page (default 10)" + page_number: + description: "Page number" + filter: + description: "Filter members by keyword" + datadog_add_team_member: + description: "Add a user to a team" + parameters: + team_id: + description: "Team ID" + required: true + user_id: + description: "User ID to add" + required: true + role: + description: "Role: admin (omit for regular member)" + datadog_update_team_member: + description: "Update a team member's role" + parameters: + team_id: + description: "Team ID" + required: true + user_id: + description: "User ID" + required: true + role: + description: "New role: admin (omit for regular member)" + datadog_remove_team_member: + description: "Remove a user from a team" + parameters: + team_id: + description: "Team ID" + required: true + user_id: + description: "User ID to remove" + required: true + datadog_get_user_team_memberships: + description: "Get all team memberships for a user. Find which teams a user belongs to." + parameters: + user_id: + description: "User ID" + required: true + datadog_list_team_links: + description: "List external links for a team. View runbooks, dashboards, and documentation links associated with a team." + parameters: + team_id: + description: "Team ID" + required: true + datadog_get_team_link: + description: "Get a specific team link. Use after list_team_links." + parameters: + team_id: + description: "Team ID" + required: true + link_id: + description: "Link ID" + required: true + datadog_create_team_link: + description: "Add a link to a team (runbook, dashboard, documentation)" + parameters: + team_id: + description: "Team ID" + required: true + label: + description: "Link label" + required: true + url: + description: "Link URL" + required: true + datadog_update_team_link: + description: "Update a team link" + parameters: + team_id: + description: "Team ID" + required: true + link_id: + description: "Link ID" + required: true + label: + description: "New label" + required: true + url: + description: "New URL" + required: true + datadog_delete_team_link: + description: "Delete a team link" + parameters: + team_id: + description: "Team ID" + required: true + link_id: + description: "Link ID" + required: true + datadog_get_team_permission_settings: + description: "Get permission settings for a team. View who can manage membership and edit the team." + parameters: + team_id: + description: "Team ID" + required: true + datadog_update_team_permission_setting: + description: "Update a team permission setting" + parameters: + team_id: + description: "Team ID" + required: true + action: + description: "Permission action: manage_membership or edit" + required: true + value: + description: "Permission value: admins, members, organization, user_access_manage, teams_manage" + required: true + datadog_search_spans: + description: "Search Datadog APM spans and distributed traces for production performance debugging. Investigate latency and service dependencies." + parameters: + query: + description: "Span search query (e.g., 'service:web-store resource_name:GET')" + required: true + from: + description: "Start time" + to: + description: "End time" + limit: + description: "Max results (default 10)" + sort: + description: "Sort: timestamp or -timestamp" + datadog_list_services: + description: "List services from the Datadog Software Catalog. View production microservices, dependencies, and ownership." + parameters: + page_size: + description: "Results per page (default 20)" + page_offset: + description: "Pagination offset" + datadog_list_oncall_schedules: + description: "List all Datadog On-Call schedules. Start here to find schedule IDs before getting details." + parameters: + include: + description: "Comma-separated related resources to include (e.g. teams)" + datadog_get_oncall_schedule: + description: "Get a Datadog On-Call schedule with layers, members, and rotation details. View who is on-call and when." + parameters: + schedule_id: + description: "On-Call schedule ID" + required: true + datadog_create_oncall_schedule: + description: 'Create a new On-Call schedule with layers, rotation intervals, and members. Use body_json with Datadog schedule create schema.' + parameters: + body_json: + description: 'JSON body matching Datadog ScheduleCreateRequest schema: {"data":{"type":"schedules","attributes":{"name":"...","time_zone":"...","layers":[{"name":"...","effective_date":"...","rotation_start":"...","interval":{"days":7},"members":[{"user":{"id":"..."}}]}]}}}' + required: true + datadog_update_oncall_schedule: + description: "Update an existing On-Call schedule (layers, members, rotations). IMPORTANT: Always include the full relationships.teams block in body_json — omitting it silently removes the team association. Fetch current schedule with get_oncall_schedule first, then modify and submit." + parameters: + schedule_id: + description: "Schedule ID" + required: true + body_json: + description: "JSON body matching Datadog ScheduleUpdateRequest schema. MUST include relationships.teams to preserve team association." + required: true + datadog_delete_oncall_schedule: + description: "Delete an On-Call schedule" + parameters: + schedule_id: + description: "Schedule ID" + required: true + datadog_get_schedule_oncall_user: + description: "Get the current on-call user for a schedule. Find who is on-call right now for a given rotation." + parameters: + schedule_id: + description: "On-Call schedule ID" + required: true + datadog_list_oncall_escalation_policies: + description: "List all Datadog On-Call escalation policies. Start here to find policy IDs before getting details." + parameters: + include: + description: "Comma-separated related resources to include (e.g. teams,steps,steps.targets)" + datadog_get_oncall_escalation_policy: + description: "Get a Datadog On-Call escalation policy with steps, targets, and notification chain. View escalation rules and timing." + parameters: + policy_id: + description: "Escalation policy ID" + required: true + datadog_create_oncall_escalation_policy: + description: 'Create a new On-Call escalation policy with steps and targets. Use body_json with Datadog escalation policy create schema.' + parameters: + body_json: + description: 'JSON body matching Datadog EscalationPolicyCreateRequest schema: {"data":{"type":"policies","attributes":{"name":"...","steps":[{"targets":[{"type":"users","id":"..."}],"escalate_after_seconds":300}]}}}' + required: true + datadog_update_oncall_escalation_policy: + description: "Update an existing On-Call escalation policy (steps, targets, timing). Fetch current policy with get_oncall_escalation_policy first, then modify and submit." + parameters: + policy_id: + description: "Escalation policy ID" + required: true + body_json: + description: "JSON body matching Datadog EscalationPolicyUpdateRequest schema" + required: true + datadog_delete_oncall_escalation_policy: + description: "Delete an On-Call escalation policy" + parameters: + policy_id: + description: "Escalation policy ID" + required: true + datadog_get_oncall_team_routing_rules: + description: "Get On-Call routing rules for a team. View how pages are routed to schedules and escalation policies." + parameters: + team_id: + description: "Team ID" + required: true + datadog_set_oncall_team_routing_rules: + description: "Set (replace) On-Call routing rules for a team. Define how pages are routed to escalation policies. Fetch current rules with get_oncall_team_routing_rules first." + parameters: + team_id: + description: "Team ID" + required: true + body_json: + description: 'JSON body matching Datadog TeamRoutingRulesRequest schema: {"data":{"type":"team_routing_rules","attributes":{"rules":[{"policy_id":"...","urgency":"high"}]}}}' + required: true + datadog_get_team_oncall_users: + description: "Get the current on-call users for a team. Find who is on-call right now across all team schedules." + parameters: + team_id: + description: "Team ID" + required: true + datadog_list_oncall_pages: + description: "List On-Call pages. Filter by status and urgency to find active or past pages." + parameters: + include: + description: "Comma-separated related resources to include" + status: + description: "Filter by page status (e.g. triggered, acknowledged, resolved)" + urgency: + description: "Filter by urgency (low or high)" + datadog_get_oncall_page: + description: "Get details of a specific On-Call page including status, responders, and timeline." + parameters: + page_id: + description: "Page UUID" + required: true + include: + description: "Comma-separated related resources to include" + datadog_create_oncall_page: + description: "Create a new On-Call page to alert responders. Page a team or user for production incidents and urgent issues." + parameters: + title: + description: "Page title" + required: true + urgency: + description: "Urgency: low or high (default high)" + target_id: + description: "Target identifier (team handle, team ID, or user ID)" + required: true + target_type: + description: "Target type: team_handle, team_id, or user_id (default team_handle)" + description: + description: "Page description with details" + tags: + description: "Comma-separated tags" + datadog_acknowledge_oncall_page: + description: "Acknowledge an On-Call page to indicate responder awareness" + parameters: + page_id: + description: "Page UUID" + required: true + datadog_escalate_oncall_page: + description: "Escalate an On-Call page to the next responder in the escalation policy" + parameters: + page_id: + description: "Page UUID" + required: true + datadog_resolve_oncall_page: + description: "Resolve an On-Call page to mark the issue as handled" + parameters: + page_id: + description: "Page UUID" + required: true + datadog_list_user_notification_channels: + description: "List On-Call notification channels for a user (email, Slack, SMS, push). View how a user receives on-call alerts." + parameters: + user_id: + description: "User ID" + required: true + datadog_create_user_notification_channel: + description: "Create a notification channel for a user (email, Slack, SMS, push)." + parameters: + user_id: + description: "User ID" + required: true + body_json: + description: "JSON body matching Datadog CreateUserNotificationChannelRequest schema" + required: true + datadog_get_user_notification_channel: + description: "Get a specific notification channel for a user." + parameters: + user_id: + description: "User ID" + required: true + channel_id: + description: "Notification channel ID" + required: true + datadog_delete_user_notification_channel: + description: "Delete a notification channel for a user." + parameters: + user_id: + description: "User ID" + required: true + channel_id: + description: "Notification channel ID" + required: true + datadog_list_user_notification_rules: + description: "List On-Call notification rules for a user. Rules define when and how a user is notified for on-call pages." + parameters: + user_id: + description: "User ID" + required: true + include: + description: "Comma-separated related resources to include" + datadog_create_user_notification_rule: + description: "Create a notification rule for a user defining when and how they are notified for on-call pages." + parameters: + user_id: + description: "User ID" + required: true + body_json: + description: "JSON body matching Datadog CreateOnCallNotificationRuleRequest schema" + required: true + datadog_get_user_notification_rule: + description: "Get a specific notification rule for a user." + parameters: + user_id: + description: "User ID" + required: true + rule_id: + description: "Notification rule ID" + required: true + include: + description: "Comma-separated related resources to include" + datadog_update_user_notification_rule: + description: "Update a notification rule for a user." + parameters: + user_id: + description: "User ID" + required: true + rule_id: + description: "Notification rule ID" + required: true + body_json: + description: "JSON body matching Datadog UpdateOnCallNotificationRuleRequest schema" + required: true + include: + description: "Comma-separated related resources to include" + datadog_delete_user_notification_rule: + description: "Delete a notification rule for a user." + parameters: + user_id: + description: "User ID" + required: true + rule_id: + description: "Notification rule ID" + required: true + datadog_get_ip_ranges: + description: "Get Datadog IP address ranges used for inbound/outbound traffic" + parameters: {} diff --git a/integrations/digitalocean/tools.go b/integrations/digitalocean/tools.go index a85d14ae..3ed7a535 100644 --- a/integrations/digitalocean/tools.go +++ b/integrations/digitalocean/tools.go @@ -1,250 +1,12 @@ package digitalocean -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Account ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_get_account"), Description: "Get current account information including email, droplet limit, and status. Start here to verify access.", - Parameters: map[string]string{}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Droplets ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_droplets"), Description: "List all droplets in the account", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page (max 200)", "tag_name": "Filter by tag"}, - }, - { - Name: mcp.ToolName("digitalocean_get_droplet"), Description: "Get details of a specific droplet", - Parameters: map[string]string{"droplet_id": "Droplet ID (integer)"}, - Required: []string{"droplet_id"}, - }, - { - Name: mcp.ToolName("digitalocean_create_droplet"), Description: "Create a new droplet. Use user_data for cloud-init bootstrapping scripts when the droplet must self-configure on first boot.", - Parameters: map[string]string{"name": "Droplet name", "region": "Region slug (e.g. nyc3)", "size": "Size slug (e.g. s-1vcpu-1gb)", "image": "Image slug or ID (e.g. ubuntu-24-04-x64)", "ssh_keys": "Comma-separated SSH key IDs or fingerprints", "tags": "Comma-separated tags", "vpc_uuid": "VPC UUID", "user_data": "Cloud-init user data script for first boot configuration"}, - Required: []string{"name", "region", "size", "image"}, - }, - { - Name: mcp.ToolName("digitalocean_delete_droplet"), Description: "Delete a droplet by ID", - Parameters: map[string]string{"droplet_id": "Droplet ID (integer)"}, - Required: []string{"droplet_id"}, - }, - { - Name: mcp.ToolName("digitalocean_reboot_droplet"), Description: "Reboot a droplet", - Parameters: map[string]string{"droplet_id": "Droplet ID (integer)"}, - Required: []string{"droplet_id"}, - }, - { - Name: mcp.ToolName("digitalocean_poweroff_droplet"), Description: "Power off a droplet (hard shutdown)", - Parameters: map[string]string{"droplet_id": "Droplet ID (integer)"}, - Required: []string{"droplet_id"}, - }, - { - Name: mcp.ToolName("digitalocean_poweron_droplet"), Description: "Power on a droplet", - Parameters: map[string]string{"droplet_id": "Droplet ID (integer)"}, - Required: []string{"droplet_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Kubernetes ────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_kubernetes_clusters"), Description: "List all Kubernetes clusters", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_kubernetes_cluster"), Description: "Get details of a specific Kubernetes cluster", - Parameters: map[string]string{"cluster_id": "Cluster UUID"}, - Required: []string{"cluster_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_kubernetes_node_pools"), Description: "List node pools for a Kubernetes cluster", - Parameters: map[string]string{"cluster_id": "Cluster UUID"}, - Required: []string{"cluster_id"}, - }, - - // ── Databases ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_databases"), Description: "List all managed database clusters", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_database"), Description: "Get details of a managed database cluster", - Parameters: map[string]string{"database_id": "Database cluster UUID"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_database_dbs"), Description: "List databases within a managed database cluster", - Parameters: map[string]string{"database_id": "Database cluster UUID"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_database_users"), Description: "List users for a managed database cluster", - Parameters: map[string]string{"database_id": "Database cluster UUID"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_database_pools"), Description: "List connection pools for a managed database cluster", - Parameters: map[string]string{"database_id": "Database cluster UUID"}, - Required: []string{"database_id"}, - }, - - // ── Networking ────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_domains"), Description: "List all domains", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_domain"), Description: "Get details of a domain", - Parameters: map[string]string{"domain_name": "Domain name (e.g. example.com)"}, - Required: []string{"domain_name"}, - }, - { - Name: mcp.ToolName("digitalocean_list_domain_records"), Description: "List DNS records for a domain", - Parameters: map[string]string{"domain_name": "Domain name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"domain_name"}, - }, - { - Name: mcp.ToolName("digitalocean_list_load_balancers"), Description: "List all load balancers", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_load_balancer"), Description: "Get details of a load balancer", - Parameters: map[string]string{"lb_id": "Load balancer UUID"}, - Required: []string{"lb_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_firewalls"), Description: "List all cloud firewalls", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_firewall"), Description: "Get details of a cloud firewall", - Parameters: map[string]string{"firewall_id": "Firewall UUID"}, - Required: []string{"firewall_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_vpcs"), Description: "List all VPCs", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_vpc"), Description: "Get details of a VPC", - Parameters: map[string]string{"vpc_id": "VPC UUID"}, - Required: []string{"vpc_id"}, - }, - - // ── Volumes ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_volumes"), Description: "List all block storage volumes", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page", "region": "Filter by region slug"}, - }, - { - Name: mcp.ToolName("digitalocean_get_volume"), Description: "Get details of a block storage volume", - Parameters: map[string]string{"volume_id": "Volume UUID"}, - Required: []string{"volume_id"}, - }, - - // ── Apps ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_apps"), Description: "List all App Platform apps", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_app"), Description: "Get details of an App Platform app including its spec and active deployment", - Parameters: map[string]string{"app_id": "App UUID"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_delete_app"), Description: "Delete an App Platform app", - Parameters: map[string]string{"app_id": "App UUID"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_restart_app"), Description: "Restart an App Platform app, optionally targeting specific components", - Parameters: map[string]string{"app_id": "App UUID", "components": "Comma-separated component names to restart (omit to restart all)"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_app_deployments"), Description: "List deployments for an App Platform app", - Parameters: map[string]string{"app_id": "App UUID", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_get_app_deployment"), Description: "Get details of a specific App Platform deployment", - Parameters: map[string]string{"app_id": "App UUID", "deployment_id": "Deployment UUID"}, - Required: []string{"app_id", "deployment_id"}, - }, - { - Name: mcp.ToolName("digitalocean_create_app_deployment"), Description: "Trigger a new deployment for an App Platform app", - Parameters: map[string]string{"app_id": "App UUID", "force_build": "Force rebuild even if no source changes (true/false)"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_get_app_logs"), Description: "Get logs for an App Platform app. Use log_type BUILD for build logs, DEPLOY for deploy logs, or RUN for runtime logs", - Parameters: map[string]string{"app_id": "App UUID", "deployment_id": "Deployment UUID (omit for active deployment)", "component": "Component name (omit for all components)", "log_type": "Log type: BUILD, DEPLOY, RUN, RUN_RESTARTED, or AUTOSCALE_EVENT", "tail_lines": "Number of log lines to return (default 100)"}, - Required: []string{"app_id", "log_type"}, - }, - { - Name: mcp.ToolName("digitalocean_get_app_health"), Description: "Get health status of all components in an App Platform app", - Parameters: map[string]string{"app_id": "App UUID"}, - Required: []string{"app_id"}, - }, - { - Name: mcp.ToolName("digitalocean_list_app_alerts"), Description: "List alerts configured for an App Platform app", - Parameters: map[string]string{"app_id": "App UUID"}, - Required: []string{"app_id"}, - }, - - // ── Extras ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("digitalocean_list_regions"), Description: "List all available regions", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("digitalocean_list_sizes"), Description: "List all available droplet sizes", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("digitalocean_list_images"), Description: "List available images (OS distributions and snapshots)", - Parameters: map[string]string{"type": "Filter by type: distribution, application, or user", "page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_ssh_keys"), Description: "List all SSH keys in the account", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_snapshots"), Description: "List all snapshots (droplet and volume)", - Parameters: map[string]string{"resource_type": "Filter: droplet or volume", "page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_projects"), Description: "List all projects", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_get_project"), Description: "Get details of a project", - Parameters: map[string]string{"project_id": "Project UUID"}, - Required: []string{"project_id"}, - }, - { - Name: mcp.ToolName("digitalocean_get_balance"), Description: "Get current account balance", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("digitalocean_list_invoices"), Description: "List billing invoices", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_cdn_endpoints"), Description: "List all CDN endpoints", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_certificates"), Description: "List all SSL/TLS certificates", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("digitalocean_list_registries"), Description: "List container registry repositories", - Parameters: map[string]string{"registry_name": "Registry name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"registry_name"}, - }, - { - Name: mcp.ToolName("digitalocean_list_tags"), Description: "List all tags", - Parameters: map[string]string{"page": "Page number", "per_page": "Results per page"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/digitalocean/tools.yaml b/integrations/digitalocean/tools.yaml new file mode 100644 index 00000000..19969416 --- /dev/null +++ b/integrations/digitalocean/tools.yaml @@ -0,0 +1,407 @@ +version: 1 +tools: + digitalocean_get_account: + description: "Get current account information including email, droplet limit, and status. Start here to verify access." + parameters: {} + + digitalocean_list_droplets: + description: "List all droplets in the account" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page (max 200)" + tag_name: + description: "Filter by tag" + + digitalocean_get_droplet: + description: "Get details of a specific droplet" + parameters: + droplet_id: + description: "Droplet ID (integer)" + required: true + + digitalocean_create_droplet: + description: "Create a new droplet" + parameters: + name: + description: "Droplet name" + required: true + region: + description: "Region slug (e.g. nyc3)" + required: true + size: + description: "Size slug (e.g. s-1vcpu-1gb)" + required: true + image: + description: "Image slug or ID (e.g. ubuntu-24-04-x64)" + required: true + ssh_keys: + description: "Comma-separated SSH key IDs or fingerprints" + tags: + description: "Comma-separated tags" + vpc_uuid: + description: "VPC UUID" + + digitalocean_delete_droplet: + description: "Delete a droplet by ID" + parameters: + droplet_id: + description: "Droplet ID (integer)" + required: true + + digitalocean_reboot_droplet: + description: "Reboot a droplet" + parameters: + droplet_id: + description: "Droplet ID (integer)" + required: true + + digitalocean_poweroff_droplet: + description: "Power off a droplet (hard shutdown)" + parameters: + droplet_id: + description: "Droplet ID (integer)" + required: true + + digitalocean_poweron_droplet: + description: "Power on a droplet" + parameters: + droplet_id: + description: "Droplet ID (integer)" + required: true + + digitalocean_list_kubernetes_clusters: + description: "List all Kubernetes clusters" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_kubernetes_cluster: + description: "Get details of a specific Kubernetes cluster" + parameters: + cluster_id: + description: "Cluster UUID" + required: true + + digitalocean_list_kubernetes_node_pools: + description: "List node pools for a Kubernetes cluster" + parameters: + cluster_id: + description: "Cluster UUID" + required: true + + digitalocean_list_databases: + description: "List all managed database clusters" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_database: + description: "Get details of a managed database cluster" + parameters: + database_id: + description: "Database cluster UUID" + required: true + + digitalocean_list_database_dbs: + description: "List databases within a managed database cluster" + parameters: + database_id: + description: "Database cluster UUID" + required: true + + digitalocean_list_database_users: + description: "List users for a managed database cluster" + parameters: + database_id: + description: "Database cluster UUID" + required: true + + digitalocean_list_database_pools: + description: "List connection pools for a managed database cluster" + parameters: + database_id: + description: "Database cluster UUID" + required: true + + digitalocean_list_domains: + description: "List all domains" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_domain: + description: "Get details of a domain" + parameters: + domain_name: + description: "Domain name (e.g. example.com)" + required: true + + digitalocean_list_domain_records: + description: "List DNS records for a domain" + parameters: + domain_name: + description: "Domain name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_load_balancers: + description: "List all load balancers" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_load_balancer: + description: "Get details of a load balancer" + parameters: + lb_id: + description: "Load balancer UUID" + required: true + + digitalocean_list_firewalls: + description: "List all cloud firewalls" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_firewall: + description: "Get details of a cloud firewall" + parameters: + firewall_id: + description: "Firewall UUID" + required: true + + digitalocean_list_vpcs: + description: "List all VPCs" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_vpc: + description: "Get details of a VPC" + parameters: + vpc_id: + description: "VPC UUID" + required: true + + digitalocean_list_volumes: + description: "List all block storage volumes" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + region: + description: "Filter by region slug" + + digitalocean_get_volume: + description: "Get details of a block storage volume" + parameters: + volume_id: + description: "Volume UUID" + required: true + + digitalocean_list_apps: + description: "List all App Platform apps" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_app: + description: "Get details of an App Platform app including its spec and active deployment" + parameters: + app_id: + description: "App UUID" + required: true + + digitalocean_delete_app: + description: "Delete an App Platform app" + parameters: + app_id: + description: "App UUID" + required: true + + digitalocean_restart_app: + description: "Restart an App Platform app, optionally targeting specific components" + parameters: + app_id: + description: "App UUID" + required: true + components: + description: "Comma-separated component names to restart (omit to restart all)" + + digitalocean_list_app_deployments: + description: "List deployments for an App Platform app" + parameters: + app_id: + description: "App UUID" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_app_deployment: + description: "Get details of a specific App Platform deployment" + parameters: + app_id: + description: "App UUID" + required: true + deployment_id: + description: "Deployment UUID" + required: true + + digitalocean_create_app_deployment: + description: "Trigger a new deployment for an App Platform app" + parameters: + app_id: + description: "App UUID" + required: true + force_build: + description: "Force rebuild even if no source changes (true/false)" + + digitalocean_get_app_logs: + description: "Get logs for an App Platform app. Use log_type BUILD for build logs, DEPLOY for deploy logs, or RUN for runtime logs" + parameters: + app_id: + description: "App UUID" + required: true + deployment_id: + description: "Deployment UUID (omit for active deployment)" + component: + description: "Component name (omit for all components)" + log_type: + description: "Log type: BUILD, DEPLOY, RUN, RUN_RESTARTED, or AUTOSCALE_EVENT" + required: true + tail_lines: + description: "Number of log lines to return (default 100)" + + digitalocean_get_app_health: + description: "Get health status of all components in an App Platform app" + parameters: + app_id: + description: "App UUID" + required: true + + digitalocean_list_app_alerts: + description: "List alerts configured for an App Platform app" + parameters: + app_id: + description: "App UUID" + required: true + + digitalocean_list_regions: + description: "List all available regions" + parameters: {} + + digitalocean_list_sizes: + description: "List all available droplet sizes" + parameters: {} + + digitalocean_list_images: + description: "List available images (OS distributions and snapshots)" + parameters: + type: + description: "Filter by type: distribution, application, or user" + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_ssh_keys: + description: "List all SSH keys in the account" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_snapshots: + description: "List all snapshots (droplet and volume)" + parameters: + resource_type: + description: "Filter: droplet or volume" + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_projects: + description: "List all projects" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_get_project: + description: "Get details of a project" + parameters: + project_id: + description: "Project UUID" + required: true + + digitalocean_get_balance: + description: "Get current account balance" + parameters: {} + + digitalocean_list_invoices: + description: "List billing invoices" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_cdn_endpoints: + description: "List all CDN endpoints" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_certificates: + description: "List all SSL/TLS certificates" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_registries: + description: "List container registry repositories" + parameters: + registry_name: + description: "Registry name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + + digitalocean_list_tags: + description: "List all tags" + parameters: + page: + description: "Page number" + per_page: + description: "Results per page" diff --git a/integrations/elasticsearch/tools.go b/integrations/elasticsearch/tools.go index 3d778ed1..e3b2e07b 100644 --- a/integrations/elasticsearch/tools.go +++ b/integrations/elasticsearch/tools.go @@ -1,356 +1,15 @@ package elasticsearch -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // --- Cluster --- - { - Name: mcp.ToolName("elasticsearch_cluster_health"), - Description: "Get cluster health status (green/yellow/red), node count, shard counts, and pending tasks", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("elasticsearch_cluster_stats"), - Description: "Get cluster-wide statistics including indices count, document count, store size, and node info", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("elasticsearch_node_stats"), - Description: "Get statistics for all nodes including JVM heap, CPU, disk, and indexing metrics", - Parameters: map[string]string{ - "node_id": "Optional node ID or name to filter (omit for all nodes)", - }, - }, - { - Name: mcp.ToolName("elasticsearch_cat_nodes"), - Description: "Get a compact summary of each node: name, IP, heap, RAM, CPU, load, role, and version", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("elasticsearch_pending_tasks"), - Description: "List pending cluster-level tasks (e.g. shard allocation, mapping updates)", - Parameters: map[string]string{}, - }, + mcp "github.com/daltoniam/switchboard" +) - // --- Indices --- - { - Name: mcp.ToolName("elasticsearch_list_indices"), - Description: "List all indices with health, status, document count, and store size. Start here for index discovery", - Parameters: map[string]string{ - "pattern": "Optional index name pattern with wildcards (e.g. 'logs-*'). Omit for all indices", - }, - }, - { - Name: mcp.ToolName("elasticsearch_get_index"), - Description: "Get full index configuration including settings, mappings, and aliases", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_create_index"), - Description: "Create a new index with optional settings (shards, replicas) and field mappings", - Parameters: map[string]string{ - "index": "Index name to create", - "settings": "Optional JSON object for index settings (e.g. {\"number_of_shards\": 1})", - "mappings": "Optional JSON object for field mappings (e.g. {\"properties\": {\"title\": {\"type\": \"text\"}}})", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_delete_index"), - Description: "Delete an index and all its data. This action is irreversible", - Parameters: map[string]string{ - "index": "Index name to delete", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_get_mapping"), - Description: "Get field mappings for an index — shows field names, types, and analyzers", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_put_mapping"), - Description: "Add new fields to an existing index mapping. Cannot change existing field types", - Parameters: map[string]string{ - "index": "Index name", - "properties": "JSON object of field definitions (e.g. {\"new_field\": {\"type\": \"keyword\"}})", - }, - Required: []string{"index", "properties"}, - }, - { - Name: mcp.ToolName("elasticsearch_get_settings"), - Description: "Get index settings (shards, replicas, refresh interval, analysis config)", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_put_settings"), - Description: "Update dynamic index settings (e.g. number_of_replicas, refresh_interval)", - Parameters: map[string]string{ - "index": "Index name", - "settings": "JSON object of settings to update (e.g. {\"index\": {\"number_of_replicas\": 2}})", - }, - Required: []string{"index", "settings"}, - }, - { - Name: mcp.ToolName("elasticsearch_index_stats"), - Description: "Get indexing, search, merge, and segment statistics for an index", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_open_index"), - Description: "Open a previously closed index to make it available for search and indexing", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_close_index"), - Description: "Close an index to reduce cluster overhead. Closed indices cannot be searched", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_refresh_index"), - Description: "Refresh an index to make recent changes visible to search immediately", - Parameters: map[string]string{ - "index": "Index name", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_forcemerge_index"), - Description: "Force merge an index to reduce segment count. Useful for read-only indices", - Parameters: map[string]string{ - "index": "Index name", - "max_segments": "Optional max number of segments to merge down to (default: 1)", - }, - Required: []string{"index"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Documents --- - { - Name: mcp.ToolName("elasticsearch_get_document"), - Description: "Get a single document by its ID from an index", - Parameters: map[string]string{ - "index": "Index name", - "id": "Document ID", - }, - Required: []string{"index", "id"}, - }, - { - Name: mcp.ToolName("elasticsearch_index_document"), - Description: "Index (create or replace) a document. If no ID is provided, one is auto-generated", - Parameters: map[string]string{ - "index": "Index name", - "id": "Optional document ID (auto-generated if omitted)", - "document": "JSON object — the document body to index", - }, - Required: []string{"index", "document"}, - }, - { - Name: mcp.ToolName("elasticsearch_update_document"), - Description: "Partially update a document by merging fields. Use 'doc' for partial updates or 'script' for scripted updates", - Parameters: map[string]string{ - "index": "Index name", - "id": "Document ID", - "doc": "JSON object of fields to merge into the existing document", - "script": "Optional Painless script for scripted updates (e.g. {\"source\": \"ctx._source.count++\"})", - }, - Required: []string{"index", "id"}, - }, - { - Name: mcp.ToolName("elasticsearch_delete_document"), - Description: "Delete a single document by ID", - Parameters: map[string]string{ - "index": "Index name", - "id": "Document ID", - }, - Required: []string{"index", "id"}, - }, - { - Name: mcp.ToolName("elasticsearch_bulk"), - Description: "Perform multiple index/create/update/delete operations in a single request. Each action is a JSON object with the operation type as key", - Parameters: map[string]string{ - "operations": "JSON array of bulk operations. Each element is {\"action\": \"index|create|update|delete\", \"index\": \"...\", \"id\": \"...\", \"doc\": {...}}", - }, - Required: []string{"operations"}, - }, - { - Name: mcp.ToolName("elasticsearch_mget"), - Description: "Get multiple documents by ID in a single request", - Parameters: map[string]string{ - "index": "Optional default index name", - "docs": "JSON array of {\"_index\": \"...\", \"_id\": \"...\"} objects. _index can be omitted if index param is set", - }, - Required: []string{"docs"}, - }, - { - Name: mcp.ToolName("elasticsearch_count"), - Description: "Count documents matching a query. Returns total count without fetching documents", - Parameters: map[string]string{ - "index": "Index name or pattern (e.g. 'logs-*')", - "query": "Optional Elasticsearch query DSL as JSON (omit to count all documents)", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_delete_by_query"), - Description: "Delete all documents matching a query. Use with caution — this is irreversible", - Parameters: map[string]string{ - "index": "Index name or pattern", - "query": "Elasticsearch query DSL as JSON to select documents for deletion", - }, - Required: []string{"index", "query"}, - }, - { - Name: mcp.ToolName("elasticsearch_update_by_query"), - Description: "Update all documents matching a query using a script", - Parameters: map[string]string{ - "index": "Index name or pattern", - "query": "Elasticsearch query DSL as JSON to select documents", - "script": "Painless script for the update (e.g. {\"source\": \"ctx._source.status = 'archived'\"})", - }, - Required: []string{"index", "query", "script"}, - }, - { - Name: mcp.ToolName("elasticsearch_reindex"), - Description: "Copy documents from one index to another, optionally transforming with a script or filtering with a query. Runs asynchronously — returns a task ID that can be monitored with elasticsearch_list_tasks", - Parameters: map[string]string{ - "source_index": "Source index name", - "dest_index": "Destination index name", - "query": "Optional query DSL to filter source documents", - "script": "Optional Painless script to transform documents during reindex", - }, - Required: []string{"source_index", "dest_index"}, - }, - - // --- Search --- - { - Name: mcp.ToolName("elasticsearch_search"), - Description: "Search documents using Elasticsearch Query DSL. Supports full-text search, filters, aggregations, sorting, and highlighting. Start here for querying data", - Parameters: map[string]string{ - "index": "Index name or pattern (e.g. 'logs-*')", - "query": "Elasticsearch query DSL as JSON (e.g. {\"match\": {\"title\": \"search term\"}})", - "size": "Max results to return (default: 10, max: 10000)", - "from": "Offset for pagination (default: 0)", - "sort": "Optional sort as JSON array (e.g. [{\"timestamp\": \"desc\"}])", - "aggs": "Optional aggregations as JSON object", - "_source": "Optional source filtering — JSON array of field names to include, or false to exclude _source", - "highlight": "Optional highlight configuration as JSON object", - }, - Required: []string{"index"}, - }, - { - Name: mcp.ToolName("elasticsearch_msearch"), - Description: "Execute multiple searches in a single request. More efficient than individual search calls", - Parameters: map[string]string{ - "searches": "JSON array of search objects, each with optional 'index' and required 'body' (query DSL)", - }, - Required: []string{"searches"}, - }, - { - Name: mcp.ToolName("elasticsearch_sql_query"), - Description: "Execute a SQL query against Elasticsearch using the SQL plugin. Useful for analysts familiar with SQL", - Parameters: map[string]string{ - "query": "SQL query string (e.g. \"SELECT * FROM my_index WHERE status = 'active' LIMIT 10\")", - "format": "Response format: json (default), csv, tsv, txt, yaml", - }, - Required: []string{"query"}, - }, - - // --- Aliases --- - { - Name: mcp.ToolName("elasticsearch_list_aliases"), - Description: "List all index aliases or aliases for a specific index", - Parameters: map[string]string{ - "index": "Optional index name to filter aliases", - }, - }, - { - Name: mcp.ToolName("elasticsearch_update_aliases"), - Description: "Atomically add or remove index aliases. Useful for zero-downtime reindexing", - Parameters: map[string]string{ - "actions": "JSON array of alias actions (e.g. [{\"add\": {\"index\": \"idx-v2\", \"alias\": \"idx\"}}, {\"remove\": {\"index\": \"idx-v1\", \"alias\": \"idx\"}}])", - }, - Required: []string{"actions"}, - }, - - // --- Templates --- - { - Name: mcp.ToolName("elasticsearch_list_index_templates"), - Description: "List all index templates with their patterns, priority, and settings", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("elasticsearch_get_index_template"), - Description: "Get a specific index template definition including patterns, settings, mappings, and aliases", - Parameters: map[string]string{ - "name": "Template name", - }, - Required: []string{"name"}, - }, - - // --- Snapshot / Restore --- - { - Name: mcp.ToolName("elasticsearch_list_snapshot_repos"), - Description: "List registered snapshot repositories and their types", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("elasticsearch_list_snapshots"), - Description: "List snapshots in a repository with status, indices, and timing", - Parameters: map[string]string{ - "repository": "Snapshot repository name", - }, - Required: []string{"repository"}, - }, - - // --- Tasks --- - { - Name: mcp.ToolName("elasticsearch_list_tasks"), - Description: "List currently running tasks (reindex, update-by-query, etc.) with progress info", - Parameters: map[string]string{ - "actions": "Optional comma-separated action patterns to filter (e.g. '*reindex*')", - }, - }, - { - Name: mcp.ToolName("elasticsearch_cancel_task"), - Description: "Cancel a running task by its task ID", - Parameters: map[string]string{ - "task_id": "Task ID (e.g. 'node_id:task_number')", - }, - Required: []string{"task_id"}, - }, - - // --- Cat APIs --- - { - Name: mcp.ToolName("elasticsearch_cat_shards"), - Description: "Get shard allocation details: index, shard number, primary/replica, state, size, node", - Parameters: map[string]string{ - "index": "Optional index name to filter", - }, - }, - { - Name: mcp.ToolName("elasticsearch_cat_allocation"), - Description: "Get disk allocation per node: shards, disk used/available, host, IP", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ // Cluster diff --git a/integrations/elasticsearch/tools.yaml b/integrations/elasticsearch/tools.yaml new file mode 100644 index 00000000..93b36c20 --- /dev/null +++ b/integrations/elasticsearch/tools.yaml @@ -0,0 +1,328 @@ +version: 1 +tools: + elasticsearch_cluster_health: + description: "Get cluster health status (green/yellow/red), node count, shard counts, and pending tasks" + parameters: {} + + elasticsearch_cluster_stats: + description: "Get cluster-wide statistics including indices count, document count, store size, and node info" + parameters: {} + + elasticsearch_node_stats: + description: "Get statistics for all nodes including JVM heap, CPU, disk, and indexing metrics" + parameters: + node_id: + description: "Optional node ID or name to filter (omit for all nodes)" + + elasticsearch_cat_nodes: + description: "Get a compact summary of each node: name, IP, heap, RAM, CPU, load, role, and version" + parameters: {} + + elasticsearch_pending_tasks: + description: "List pending cluster-level tasks (e.g. shard allocation, mapping updates)" + parameters: {} + + elasticsearch_list_indices: + description: "List all indices with health, status, document count, and store size. Start here for index discovery" + parameters: + pattern: + description: "Optional index name pattern with wildcards (e.g. 'logs-*'). Omit for all indices" + + elasticsearch_get_index: + description: "Get full index configuration including settings, mappings, and aliases" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_create_index: + description: "Create a new index with optional settings (shards, replicas) and field mappings" + parameters: + index: + description: "Index name to create" + required: true + settings: + description: 'Optional JSON object for index settings (e.g. {"number_of_shards": 1})' + mappings: + description: 'Optional JSON object for field mappings (e.g. {"properties": {"title": {"type": "text"}}})' + + elasticsearch_delete_index: + description: "Delete an index and all its data. This action is irreversible" + parameters: + index: + description: "Index name to delete" + required: true + + elasticsearch_get_mapping: + description: "Get field mappings for an index — shows field names, types, and analyzers" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_put_mapping: + description: "Add new fields to an existing index mapping. Cannot change existing field types" + parameters: + index: + description: "Index name" + required: true + properties: + description: 'JSON object of field definitions (e.g. {"new_field": {"type": "keyword"}})' + required: true + + elasticsearch_get_settings: + description: "Get index settings (shards, replicas, refresh interval, analysis config)" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_put_settings: + description: "Update dynamic index settings (e.g. number_of_replicas, refresh_interval)" + parameters: + index: + description: "Index name" + required: true + settings: + description: 'JSON object of settings to update (e.g. {"index": {"number_of_replicas": 2}})' + required: true + + elasticsearch_index_stats: + description: "Get indexing, search, merge, and segment statistics for an index" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_open_index: + description: "Open a previously closed index to make it available for search and indexing" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_close_index: + description: "Close an index to reduce cluster overhead. Closed indices cannot be searched" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_refresh_index: + description: "Refresh an index to make recent changes visible to search immediately" + parameters: + index: + description: "Index name" + required: true + + elasticsearch_forcemerge_index: + description: "Force merge an index to reduce segment count. Useful for read-only indices" + parameters: + index: + description: "Index name" + required: true + max_segments: + description: "Optional max number of segments to merge down to (default: 1)" + + elasticsearch_get_document: + description: "Get a single document by its ID from an index" + parameters: + index: + description: "Index name" + required: true + id: + description: "Document ID" + required: true + + elasticsearch_index_document: + description: "Index (create or replace) a document. If no ID is provided, one is auto-generated" + parameters: + index: + description: "Index name" + required: true + id: + description: "Optional document ID (auto-generated if omitted)" + document: + description: "JSON object — the document body to index" + required: true + + elasticsearch_update_document: + description: "Partially update a document by merging fields. Use 'doc' for partial updates or 'script' for scripted updates" + parameters: + index: + description: "Index name" + required: true + id: + description: "Document ID" + required: true + doc: + description: "JSON object of fields to merge into the existing document" + script: + description: 'Optional Painless script for scripted updates (e.g. {"source": "ctx._source.count++"})' + + elasticsearch_delete_document: + description: "Delete a single document by ID" + parameters: + index: + description: "Index name" + required: true + id: + description: "Document ID" + required: true + + elasticsearch_bulk: + description: "Perform multiple index/create/update/delete operations in a single request. Each action is a JSON object with the operation type as key" + parameters: + operations: + description: 'JSON array of bulk operations. Each element is {"action": "index|create|update|delete", "index": "...", "id": "...", "doc": {...}}' + required: true + + elasticsearch_mget: + description: "Get multiple documents by ID in a single request" + parameters: + index: + description: "Optional default index name" + docs: + description: 'JSON array of {"_index": "...", "_id": "..."} objects. _index can be omitted if index param is set' + required: true + + elasticsearch_count: + description: "Count documents matching a query. Returns total count without fetching documents" + parameters: + index: + description: "Index name or pattern (e.g. 'logs-*')" + required: true + query: + description: "Optional Elasticsearch query DSL as JSON (omit to count all documents)" + + elasticsearch_delete_by_query: + description: "Delete all documents matching a query. Use with caution — this is irreversible" + parameters: + index: + description: "Index name or pattern" + required: true + query: + description: "Elasticsearch query DSL as JSON to select documents for deletion" + required: true + + elasticsearch_update_by_query: + description: "Update all documents matching a query using a script" + parameters: + index: + description: "Index name or pattern" + required: true + query: + description: "Elasticsearch query DSL as JSON to select documents" + required: true + script: + description: "Painless script for the update (e.g. {\"source\": \"ctx._source.status = 'archived'\"})" + required: true + + elasticsearch_reindex: + description: "Copy documents from one index to another, optionally transforming with a script or filtering with a query. Runs asynchronously — returns a task ID that can be monitored with elasticsearch_list_tasks" + parameters: + source_index: + description: "Source index name" + required: true + dest_index: + description: "Destination index name" + required: true + query: + description: "Optional query DSL to filter source documents" + script: + description: "Optional Painless script to transform documents during reindex" + + elasticsearch_search: + description: "Search documents using Elasticsearch Query DSL. Supports full-text search, filters, aggregations, sorting, and highlighting. Start here for querying data" + parameters: + index: + description: "Index name or pattern (e.g. 'logs-*')" + required: true + query: + description: 'Elasticsearch query DSL as JSON (e.g. {"match": {"title": "search term"}})' + size: + description: "Max results to return (default: 10, max: 10000)" + from: + description: "Offset for pagination (default: 0)" + sort: + description: 'Optional sort as JSON array (e.g. [{"timestamp": "desc"}])' + aggs: + description: "Optional aggregations as JSON object" + _source: + description: "Optional source filtering — JSON array of field names to include, or false to exclude _source" + highlight: + description: "Optional highlight configuration as JSON object" + + elasticsearch_msearch: + description: "Execute multiple searches in a single request. More efficient than individual search calls" + parameters: + searches: + description: "JSON array of search objects, each with optional 'index' and required 'body' (query DSL)" + required: true + + elasticsearch_sql_query: + description: "Execute a SQL query against Elasticsearch using the SQL plugin. Useful for analysts familiar with SQL" + parameters: + query: + description: "SQL query string (e.g. \"SELECT * FROM my_index WHERE status = 'active' LIMIT 10\")" + required: true + format: + description: "Response format: json (default), csv, tsv, txt, yaml" + + elasticsearch_list_aliases: + description: "List all index aliases or aliases for a specific index" + parameters: + index: + description: "Optional index name to filter aliases" + + elasticsearch_update_aliases: + description: "Atomically add or remove index aliases. Useful for zero-downtime reindexing" + parameters: + actions: + description: 'JSON array of alias actions (e.g. [{"add": {"index": "idx-v2", "alias": "idx"}}, {"remove": {"index": "idx-v1", "alias": "idx"}}])' + required: true + + elasticsearch_list_index_templates: + description: "List all index templates with their patterns, priority, and settings" + parameters: {} + + elasticsearch_get_index_template: + description: "Get a specific index template definition including patterns, settings, mappings, and aliases" + parameters: + name: + description: "Template name" + required: true + + elasticsearch_list_snapshot_repos: + description: "List registered snapshot repositories and their types" + parameters: {} + + elasticsearch_list_snapshots: + description: "List snapshots in a repository with status, indices, and timing" + parameters: + repository: + description: "Snapshot repository name" + required: true + + elasticsearch_list_tasks: + description: "List currently running tasks (reindex, update-by-query, etc.) with progress info" + parameters: + actions: + description: "Optional comma-separated action patterns to filter (e.g. '*reindex*')" + + elasticsearch_cancel_task: + description: "Cancel a running task by its task ID" + parameters: + task_id: + description: "Task ID (e.g. 'node_id:task_number')" + required: true + + elasticsearch_cat_shards: + description: "Get shard allocation details: index, shard number, primary/replica, state, size, node" + parameters: + index: + description: "Optional index name to filter" + + elasticsearch_cat_allocation: + description: "Get disk allocation per node: shards, disk used/available, host, IP" + parameters: {} diff --git a/integrations/fly/tools.go b/integrations/fly/tools.go index 36eef9c1..023e816b 100644 --- a/integrations/fly/tools.go +++ b/integrations/fly/tools.go @@ -1,212 +1,12 @@ package fly -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Apps ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("fly_list_apps"), Description: "List all Fly.io apps in an organization. Start here for most workflows — returns app names needed by other tools", - Parameters: map[string]string{"org_slug": "Organization slug (e.g. 'personal')"}, - Required: []string{"org_slug"}, - }, - { - Name: mcp.ToolName("fly_get_app"), Description: "Get details of a Fly.io app including status and organization info", - Parameters: map[string]string{"app_name": "App name"}, - Required: []string{"app_name"}, - }, - { - Name: mcp.ToolName("fly_create_app"), Description: "Create a new Fly.io app in an organization", - Parameters: map[string]string{ - "app_name": "Name for the new app", - "org_slug": "Organization slug (e.g. 'personal')", - "network": "Optional IPv6 private network name to segment the app onto", - }, - Required: []string{"app_name", "org_slug"}, - }, - { - Name: mcp.ToolName("fly_delete_app"), Description: "Delete a Fly.io app. Use force=true to stop all Machines first", - Parameters: map[string]string{ - "app_name": "App name to delete", - "force": "Force stop all Machines and delete immediately (true/false)", - }, - Required: []string{"app_name"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Machines ────────────────────────────────────────────────── - { - Name: mcp.ToolName("fly_list_machines"), Description: "List all Machines in a Fly.io app. Returns IDs, state, region, and image info. Use summary=true for lighter response", - Parameters: map[string]string{ - "app_name": "App name", - "include_deleted": "Include deleted machines (true/false)", - "region": "Filter by region code (e.g. 'ord', 'iad')", - "state": "Comma-separated states to filter: created, started, stopped, suspended", - "summary": "Only return summary info, omit config/checks/events (true/false)", - }, - Required: []string{"app_name"}, - }, - { - Name: mcp.ToolName("fly_get_machine"), Description: "Get full details of a specific Machine including config, events, and checks", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_create_machine"), Description: "Create a new Machine in a Fly.io app. Specify image, resources (guest), region, and optional services/mounts", - Parameters: map[string]string{ - "app_name": "App name", - "name": "Optional machine name", - "region": "Region code (e.g. 'ord', 'iad', 'lhr')", - "config": "Machine config object with: image (required), guest {cpus, memory_mb, cpu_kind}, env {}, services [], mounts [], auto_destroy, restart {policy}, metadata {}", - }, - Required: []string{"app_name", "config"}, - }, - { - Name: mcp.ToolName("fly_update_machine"), Description: "Update a Machine's configuration (image, resources, env, services, etc.)", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - "config": "Updated machine config object", - }, - Required: []string{"app_name", "machine_id", "config"}, - }, - { - Name: mcp.ToolName("fly_delete_machine"), Description: "Delete a Machine. Use force=true to kill a running machine", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - "force": "Force kill if running (true/false)", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_start_machine"), Description: "Start a stopped Machine", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_stop_machine"), Description: "Stop a running Machine", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_restart_machine"), Description: "Restart a Machine", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_signal_machine"), Description: "Send a Unix signal to a Machine process (e.g. SIGTERM, SIGKILL, SIGHUP)", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - "signal": "Signal name (SIGTERM, SIGKILL, SIGHUP, SIGUSR1, SIGUSR2, etc.)", - }, - Required: []string{"app_name", "machine_id", "signal"}, - }, - { - Name: mcp.ToolName("fly_wait_machine"), Description: "Wait for a Machine to reach a specific state. Blocks until state is reached or timeout", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - "state": "Target state: started, stopped, suspended, destroyed (default: started)", - "timeout": "Timeout in seconds (default: 60)", - }, - Required: []string{"app_name", "machine_id"}, - }, - { - Name: mcp.ToolName("fly_exec_machine"), Description: "Execute a command inside a running Machine and return stdout/stderr", - Parameters: map[string]string{ - "app_name": "App name", - "machine_id": "Machine ID", - "command": "Command to execute as an array of strings (e.g. [\"ls\", \"-la\"])", - }, - Required: []string{"app_name", "machine_id", "command"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Volumes ────────────────────────────────────────────────── - { - Name: mcp.ToolName("fly_list_volumes"), Description: "List all persistent volumes attached to a Fly.io app", - Parameters: map[string]string{"app_name": "App name"}, - Required: []string{"app_name"}, - }, - { - Name: mcp.ToolName("fly_get_volume"), Description: "Get details of a specific volume including size, region, and attached machine", - Parameters: map[string]string{ - "app_name": "App name", - "volume_id": "Volume ID", - }, - Required: []string{"app_name", "volume_id"}, - }, - { - Name: mcp.ToolName("fly_create_volume"), Description: "Create a persistent volume in a Fly.io app. Volumes are region-specific", - Parameters: map[string]string{ - "app_name": "App name", - "name": "Volume name", - "region": "Region code (e.g. 'ord')", - "size_gb": "Size in GB (default: 1)", - "encrypted": "Encrypt the volume (true/false, default: true)", - "snapshot_retention": "Number of snapshots to retain", - "auto_backup_enabled": "Enable automatic backups (true/false)", - }, - Required: []string{"app_name", "name", "region"}, - }, - { - Name: mcp.ToolName("fly_update_volume"), Description: "Update a volume's size or snapshot settings", - Parameters: map[string]string{ - "app_name": "App name", - "volume_id": "Volume ID", - "snapshot_retention": "Number of snapshots to retain", - "auto_backup_enabled": "Enable automatic backups (true/false)", - }, - Required: []string{"app_name", "volume_id"}, - }, - { - Name: mcp.ToolName("fly_delete_volume"), Description: "Delete a persistent volume. Volume must not be attached to a running machine", - Parameters: map[string]string{ - "app_name": "App name", - "volume_id": "Volume ID", - }, - Required: []string{"app_name", "volume_id"}, - }, - { - Name: mcp.ToolName("fly_list_volume_snapshots"), Description: "List snapshots for a specific volume", - Parameters: map[string]string{ - "app_name": "App name", - "volume_id": "Volume ID", - }, - Required: []string{"app_name", "volume_id"}, - }, - - // ── Secrets ────────────────────────────────────────────────── - { - Name: mcp.ToolName("fly_list_secrets"), Description: "List all secrets for a Fly.io app. Returns names and digests only — values are never exposed", - Parameters: map[string]string{"app_name": "App name"}, - Required: []string{"app_name"}, - }, - { - Name: mcp.ToolName("fly_set_secrets"), Description: "Set one or more secrets on a Fly.io app. Machines will be restarted to pick up changes", - Parameters: map[string]string{ - "app_name": "App name", - "secrets": "Key-value map of secrets to set (e.g. {\"DATABASE_URL\": \"postgres://...\"})", - }, - Required: []string{"app_name", "secrets"}, - }, - { - Name: mcp.ToolName("fly_unset_secrets"), Description: "Remove one or more secrets from a Fly.io app. Machines will be restarted", - Parameters: map[string]string{ - "app_name": "App name", - "keys": "Array of secret names to remove", - }, - Required: []string{"app_name", "keys"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/fly/tools.yaml b/integrations/fly/tools.yaml new file mode 100644 index 00000000..f781ebe0 --- /dev/null +++ b/integrations/fly/tools.yaml @@ -0,0 +1,246 @@ +version: 1 +tools: + fly_list_apps: + description: "List all Fly.io apps in an organization. Start here for most workflows — returns app names needed by other tools" + parameters: + org_slug: + description: "Organization slug (e.g. 'personal')" + required: true + fly_get_app: + description: "Get details of a Fly.io app including status and organization info" + parameters: + app_name: + description: "App name" + required: true + fly_create_app: + description: "Create a new Fly.io app in an organization" + parameters: + app_name: + description: "Name for the new app" + required: true + org_slug: + description: "Organization slug (e.g. 'personal')" + required: true + network: + description: "Optional IPv6 private network name to segment the app onto" + fly_delete_app: + description: "Delete a Fly.io app. Use force=true to stop all Machines first" + parameters: + app_name: + description: "App name to delete" + required: true + force: + description: "Force stop all Machines and delete immediately (true/false)" + fly_list_machines: + description: "List all Machines in a Fly.io app. Returns IDs, state, region, and image info. Use summary=true for lighter response" + parameters: + app_name: + description: "App name" + required: true + include_deleted: + description: "Include deleted machines (true/false)" + region: + description: "Filter by region code (e.g. 'ord', 'iad')" + state: + description: "Comma-separated states to filter: created, started, stopped, suspended" + summary: + description: "Only return summary info, omit config/checks/events (true/false)" + fly_get_machine: + description: "Get full details of a specific Machine including config, events, and checks" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + fly_create_machine: + description: "Create a new Machine in a Fly.io app. Specify image, resources (guest), region, and optional services/mounts" + parameters: + app_name: + description: "App name" + required: true + name: + description: "Optional machine name" + region: + description: "Region code (e.g. 'ord', 'iad', 'lhr')" + config: + description: "Machine config object with: image (required), guest {cpus, memory_mb, cpu_kind}, env {}, services [], mounts [], auto_destroy, restart {policy}, metadata {}" + required: true + fly_update_machine: + description: "Update a Machine's configuration (image, resources, env, services, etc.)" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + config: + description: "Updated machine config object" + required: true + fly_delete_machine: + description: "Delete a Machine. Use force=true to kill a running machine" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + force: + description: "Force kill if running (true/false)" + fly_start_machine: + description: "Start a stopped Machine" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + fly_stop_machine: + description: "Stop a running Machine" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + fly_restart_machine: + description: "Restart a Machine" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + fly_signal_machine: + description: "Send a Unix signal to a Machine process (e.g. SIGTERM, SIGKILL, SIGHUP)" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + signal: + description: "Signal name (SIGTERM, SIGKILL, SIGHUP, SIGUSR1, SIGUSR2, etc.)" + required: true + fly_wait_machine: + description: "Wait for a Machine to reach a specific state. Blocks until state is reached or timeout" + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + state: + description: "Target state: started, stopped, suspended, destroyed (default: started)" + timeout: + description: "Timeout in seconds (default: 60)" + fly_exec_machine: + description: 'Execute a command inside a running Machine and return stdout/stderr' + parameters: + app_name: + description: "App name" + required: true + machine_id: + description: "Machine ID" + required: true + command: + description: 'Command to execute as an array of strings (e.g. ["ls", "-la"])' + required: true + fly_list_volumes: + description: "List all persistent volumes attached to a Fly.io app" + parameters: + app_name: + description: "App name" + required: true + fly_get_volume: + description: "Get details of a specific volume including size, region, and attached machine" + parameters: + app_name: + description: "App name" + required: true + volume_id: + description: "Volume ID" + required: true + fly_create_volume: + description: "Create a persistent volume in a Fly.io app. Volumes are region-specific" + parameters: + app_name: + description: "App name" + required: true + name: + description: "Volume name" + required: true + region: + description: "Region code (e.g. 'ord')" + required: true + size_gb: + description: "Size in GB (default: 1)" + encrypted: + description: "Encrypt the volume (true/false, default: true)" + snapshot_retention: + description: "Number of snapshots to retain" + auto_backup_enabled: + description: "Enable automatic backups (true/false)" + fly_update_volume: + description: "Update a volume's size or snapshot settings" + parameters: + app_name: + description: "App name" + required: true + volume_id: + description: "Volume ID" + required: true + snapshot_retention: + description: "Number of snapshots to retain" + auto_backup_enabled: + description: "Enable automatic backups (true/false)" + fly_delete_volume: + description: "Delete a persistent volume. Volume must not be attached to a running machine" + parameters: + app_name: + description: "App name" + required: true + volume_id: + description: "Volume ID" + required: true + fly_list_volume_snapshots: + description: "List snapshots for a specific volume" + parameters: + app_name: + description: "App name" + required: true + volume_id: + description: "Volume ID" + required: true + fly_list_secrets: + description: "List all secrets for a Fly.io app. Returns names and digests only — values are never exposed" + parameters: + app_name: + description: "App name" + required: true + fly_set_secrets: + description: "Set one or more secrets on a Fly.io app. Machines will be restarted to pick up changes" + parameters: + app_name: + description: "App name" + required: true + secrets: + description: 'Key-value map of secrets to set (e.g. {"DATABASE_URL": "postgres://..."})' + required: true + fly_unset_secrets: + description: "Remove one or more secrets from a Fly.io app. Machines will be restarted" + parameters: + app_name: + description: "App name" + required: true + keys: + description: "Array of secret names to remove" + required: true diff --git a/integrations/gcal/tools.go b/integrations/gcal/tools.go index f9d787f5..f4875c9c 100644 --- a/integrations/gcal/tools.go +++ b/integrations/gcal/tools.go @@ -1,326 +1,12 @@ package gcal -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Events ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcal_list_events"), Description: "List events on a calendar with filters for time range, search query, recurrence, and updates. Start here for browsing the schedule, finding meetings, or checking what's on the calendar.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary' for the user's primary calendar)", - "q": "Free-text search across summary, description, location, attendees", - "time_min": "Lower bound (inclusive) RFC3339 timestamp, e.g. '2024-03-15T00:00:00Z'", - "time_max": "Upper bound (exclusive) RFC3339 timestamp", - "single_events": "Expand recurring events into individual instances (true/false, default false)", - "order_by": "Order: 'startTime' (requires single_events=true) or 'updated'", - "max_results": "Max results per page (default 250, max 2500)", - "page_token": "Token for next page", - "show_deleted": "Include cancelled events (true/false)", - "updated_min": "Only events updated at/after this RFC3339 timestamp", - "ical_uid": "Filter to a specific iCalendar UID", - "private_extended_property": "Repeated propertyName=value pairs, comma-separated, for private extended-property filters", - "shared_extended_property": "Repeated propertyName=value pairs, comma-separated, for shared extended-property filters", - "time_zone": "Time zone used in response (default: calendar's time zone)", - }, - }, - { - Name: mcp.ToolName("gcal_get_event"), Description: "Get a single calendar event by ID. Read the full meeting details including attendees, location, attachments, and conference data.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "event_id": "Event ID", - "time_zone": "Time zone used in response (default: calendar's time zone)", - }, - Required: []string{"event_id"}, - }, - { - Name: mcp.ToolName("gcal_create_event"), Description: "Create a calendar event. Schedule a meeting or appointment with attendees, time, location, and notifications.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "summary": "Event title", - "description": "Event description (plain text or HTML)", - "location": "Event location", - "start": "Start time RFC3339 (e.g. '2024-03-15T14:00:00-07:00') OR YYYY-MM-DD for all-day", - "end": "End time RFC3339 OR YYYY-MM-DD for all-day (exclusive)", - "time_zone": "Time zone for start/end (e.g. 'America/Los_Angeles')", - "attendees": "Comma-separated attendee emails", - "recurrence": "Comma-separated RRULE/EXRULE/RDATE/EXDATE lines (e.g. 'RRULE:FREQ=WEEKLY;COUNT=10')", - "send_updates": "Send notifications: all, externalOnly, none (default none)", - "conference_data_version": "Set to '1' to enable conferenceData (e.g. auto-create Google Meet link via 'create_meet=true')", - "create_meet": "If true, attaches a Google Meet conference (requires conference_data_version=1)", - "reminders_use_default": "Use the calendar's default reminders (true/false)", - "visibility": "Visibility: default, public, private, confidential", - "transparency": "Show as: opaque (busy) or transparent (free)", - "color_id": "Color ID from gcal_get_colors event palette", - "body": "Raw JSON body — overrides all other fields. Use for advanced cases (attachments, custom reminders, extendedProperties)", - }, - }, - { - Name: mcp.ToolName("gcal_update_event"), Description: "Replace a calendar event with PUT semantics — all fields must be provided. Prefer gcal_patch_event for partial updates.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "event_id": "Event ID", - "body": "Full JSON event resource (replaces the entire event)", - "send_updates": "Send notifications: all, externalOnly, none (default none)", - }, - Required: []string{"event_id", "body"}, - }, - { - Name: mcp.ToolName("gcal_patch_event"), Description: "Partially update a calendar event. Edit a meeting's title, time, attendees, or other fields without replacing the entire event.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "event_id": "Event ID", - "summary": "New title (optional)", - "description": "New description (optional)", - "location": "New location (optional)", - "start": "New start time RFC3339 or YYYY-MM-DD (optional)", - "end": "New end time RFC3339 or YYYY-MM-DD (optional)", - "time_zone": "Time zone for start/end", - "attendees": "Replace attendee list with comma-separated emails", - "recurrence": "Replace recurrence rules (comma-separated lines)", - "send_updates": "Send notifications: all, externalOnly, none (default none)", - "visibility": "Visibility: default, public, private, confidential", - "transparency": "Show as: opaque or transparent", - "color_id": "Color ID", - "body": "Raw JSON patch body — overrides other fields", - }, - Required: []string{"event_id"}, - }, - { - Name: mcp.ToolName("gcal_delete_event"), Description: "Delete a calendar event. Cancel a meeting; optionally notify attendees.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "event_id": "Event ID", - "send_updates": "Send cancellation notifications: all, externalOnly, none (default none)", - }, - Required: []string{"event_id"}, - }, - { - Name: mcp.ToolName("gcal_move_event"), Description: "Move an event to a different calendar (re-home a meeting from personal to shared, for example).", - Parameters: map[string]string{ - "calendar_id": "Source calendar ID (defaults to 'primary')", - "event_id": "Event ID", - "destination": "Destination calendar ID", - "send_updates": "Send notifications: all, externalOnly, none (default none)", - }, - Required: []string{"event_id", "destination"}, - }, - { - Name: mcp.ToolName("gcal_list_instances"), Description: "List the individual instances of a recurring event. Expand a weekly meeting into the actual occurrences.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "event_id": "Recurring event ID", - "time_min": "Lower bound RFC3339", - "time_max": "Upper bound RFC3339", - "max_results": "Max results per page", - "page_token": "Token for next page", - "show_deleted": "Include cancelled instances (true/false)", - "original_start": "Original start time of a specific instance (RFC3339)", - "time_zone": "Time zone used in response", - }, - Required: []string{"event_id"}, - }, - { - Name: mcp.ToolName("gcal_quick_add_event"), Description: "Create an event from a natural-language string (e.g. 'Lunch with Sarah Friday 1pm'). Quick way to schedule when you have a free-form text description.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "text": "Natural-language event description", - "send_updates": "Send notifications: all, externalOnly, none (default none)", - }, - Required: []string{"text"}, - }, - { - Name: mcp.ToolName("gcal_import_event"), Description: "Import an event from an external calendar (preserves the iCalUID, organizer, and original creation time). Use for syncing events; use create_event for new events.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "body": "Full JSON event resource including iCalUID and organizer", - "conference_data_version": "Set to '1' to preserve conferenceData", - "supports_attachments": "Whether the API client supports event attachments (true/false)", - }, - Required: []string{"body"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── CalendarList (the user's subscribed calendars) ────────────── - { - Name: mcp.ToolName("gcal_list_calendars"), Description: "List the calendars in the user's calendar list. Discover which calendars are available to query (primary, secondary, shared, subscribed).", - Parameters: map[string]string{ - "max_results": "Max results per page (default 100, max 250)", - "page_token": "Token for next page", - "show_deleted": "Include deleted entries (true/false)", - "show_hidden": "Include hidden entries (true/false)", - "min_access_role": "Filter by minimum access role: freeBusyReader, reader, writer, owner", - }, - }, - { - Name: mcp.ToolName("gcal_get_calendar_subscription"), Description: "Get a single calendar-list entry (the user-specific view of a subscribed calendar: color, hidden state, default reminders).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID", - }, - Required: []string{"calendar_id"}, - }, - { - Name: mcp.ToolName("gcal_subscribe_calendar"), Description: "Subscribe to an existing calendar by inserting it into the user's calendar list.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID to subscribe to", - "color_rgb_format": "Set to 'true' to use the color_id/background_color/foreground_color values", - "background_color": "Background color hex (e.g. '#ffffff')", - "foreground_color": "Foreground color hex", - "color_id": "Color ID from gcal_get_colors calendar palette", - "hidden": "Hide from UI (true/false)", - "selected": "Show in calendar view (true/false)", - "summary_override": "Custom display name for this user", - }, - Required: []string{"calendar_id"}, - }, - { - Name: mcp.ToolName("gcal_update_calendar_subscription"), Description: "Update the user-specific properties of a calendar subscription (color, hidden state, summary override).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID", - "color_rgb_format": "Set to 'true' to use the color hex values", - "background_color": "Background color hex", - "foreground_color": "Foreground color hex", - "color_id": "Color ID", - "hidden": "Hide from UI (true/false)", - "selected": "Show in calendar view (true/false)", - "summary_override": "Custom display name for this user", - "body": "Raw JSON body — overrides other fields", - }, - Required: []string{"calendar_id"}, - }, - { - Name: mcp.ToolName("gcal_unsubscribe_calendar"), Description: "Remove a calendar from the user's calendar list (unsubscribe — does not delete the underlying calendar).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID", - }, - Required: []string{"calendar_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Calendars (the underlying calendar resource) ──────────────── - { - Name: mcp.ToolName("gcal_get_calendar"), Description: "Get the metadata of a calendar resource (summary, description, time zone, location).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - }, - }, - { - Name: mcp.ToolName("gcal_create_calendar"), Description: "Create a new secondary calendar owned by the user.", - Parameters: map[string]string{ - "summary": "Calendar title", - "description": "Calendar description", - "location": "Geographic location of the calendar", - "time_zone": "Time zone (e.g. 'America/Los_Angeles')", - "body": "Raw JSON body — overrides other fields", - }, - Required: []string{"summary"}, - }, - { - Name: mcp.ToolName("gcal_update_calendar"), Description: "Update a calendar's metadata (summary, description, time zone).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID", - "summary": "New calendar title", - "description": "New description", - "location": "New location", - "time_zone": "New time zone", - "body": "Raw JSON patch body — overrides other fields", - }, - Required: []string{"calendar_id"}, - }, - { - Name: mcp.ToolName("gcal_delete_calendar"), Description: "Permanently delete a secondary calendar (cannot delete primary calendars; use gcal_clear_calendar for primary).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID", - }, - Required: []string{"calendar_id"}, - }, - { - Name: mcp.ToolName("gcal_clear_calendar"), Description: "Clear all events from the user's primary calendar. Use only for the primary calendar; secondaries should be deleted instead.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - }, - }, - - // ── ACL (sharing rules) ───────────────────────────────────────── - { - Name: mcp.ToolName("gcal_list_acl"), Description: "List the access control rules (sharing) for a calendar.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "max_results": "Max results per page", - "page_token": "Token for next page", - "show_deleted": "Include deleted ACL entries (true/false)", - }, - }, - { - Name: mcp.ToolName("gcal_get_acl"), Description: "Get a single access control rule by ID.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "rule_id": "ACL rule ID", - }, - Required: []string{"rule_id"}, - }, - { - Name: mcp.ToolName("gcal_create_acl"), Description: "Grant access to a calendar. Share with a specific user, group, or domain.", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "role": "Role: none, freeBusyReader, reader, writer, owner", - "scope_type": "Scope type: default, user, group, domain", - "scope_value": "Scope value (email for user/group, domain name for domain; omit for 'default')", - "send_notifications": "Send notification email (true/false)", - "body": "Raw JSON body — overrides other fields", - }, - Required: []string{"role", "scope_type"}, - }, - { - Name: mcp.ToolName("gcal_update_acl"), Description: "Update a calendar access control rule (change the granted role).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "rule_id": "ACL rule ID", - "role": "New role: none, freeBusyReader, reader, writer, owner", - "scope_type": "Scope type (must match existing): default, user, group, domain", - "scope_value": "Scope value", - "body": "Raw JSON body — overrides other fields", - }, - Required: []string{"rule_id"}, - }, - { - Name: mcp.ToolName("gcal_delete_acl"), Description: "Revoke a calendar access control rule (unshare).", - Parameters: map[string]string{ - "calendar_id": "Calendar ID (defaults to 'primary')", - "rule_id": "ACL rule ID", - }, - Required: []string{"rule_id"}, - }, - - // ── Freebusy ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcal_query_freebusy"), Description: "Check availability across calendars in a time range. Returns busy intervals — use to find open time slots for scheduling meetings.", - Parameters: map[string]string{ - "time_min": "Lower bound RFC3339", - "time_max": "Upper bound RFC3339", - "items": "Comma-separated calendar IDs (or group IDs) to query", - "time_zone": "Time zone for the response", - "calendar_expansion_max": "Max number of calendars to expand from groups (default 50)", - "group_expansion_max": "Max members per group expansion (default 100)", - "body": "Raw JSON body — overrides other fields", - }, - Required: []string{"time_min", "time_max", "items"}, - }, - - // ── Settings + colors ─────────────────────────────────────────── - { - Name: mcp.ToolName("gcal_list_settings"), Description: "List the user's Calendar settings (week start, timezone, default event length, working hours).", - Parameters: map[string]string{ - "max_results": "Max results per page", - "page_token": "Token for next page", - }, - }, - { - Name: mcp.ToolName("gcal_get_setting"), Description: "Get a single user Calendar setting by ID.", - Parameters: map[string]string{ - "setting_id": "Setting ID (e.g. 'timezone', 'weekStart', 'defaultEventLength')", - }, - Required: []string{"setting_id"}, - }, - { - Name: mcp.ToolName("gcal_get_colors"), Description: "Get the color palettes available for calendars and events. Returns the mapping of color IDs to hex values.", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gcal/tools.yaml b/integrations/gcal/tools.yaml new file mode 100644 index 00000000..b1a3394a --- /dev/null +++ b/integrations/gcal/tools.yaml @@ -0,0 +1,402 @@ +version: 1 +tools: + gcal_list_events: + description: "List events on a calendar with filters for time range, search query, recurrence, and updates. Start here for browsing the schedule, finding meetings, or checking what's on the calendar." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary' for the user's primary calendar)" + q: + description: "Free-text search across summary, description, location, attendees" + time_min: + description: "Lower bound (inclusive) RFC3339 timestamp, e.g. '2024-03-15T00:00:00Z'" + time_max: + description: "Upper bound (exclusive) RFC3339 timestamp" + single_events: + description: "Expand recurring events into individual instances (true/false, default false)" + order_by: + description: "Order: 'startTime' (requires single_events=true) or 'updated'" + max_results: + description: "Max results per page (default 250, max 2500)" + page_token: + description: "Token for next page" + show_deleted: + description: "Include cancelled events (true/false)" + updated_min: + description: "Only events updated at/after this RFC3339 timestamp" + ical_uid: + description: "Filter to a specific iCalendar UID" + private_extended_property: + description: "Repeated propertyName=value pairs, comma-separated, for private extended-property filters" + shared_extended_property: + description: "Repeated propertyName=value pairs, comma-separated, for shared extended-property filters" + time_zone: + description: "Time zone used in response (default: calendar's time zone)" + gcal_get_event: + description: "Get a single calendar event by ID. Read the full meeting details including attendees, location, attachments, and conference data." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + event_id: + description: "Event ID" + required: true + time_zone: + description: "Time zone used in response (default: calendar's time zone)" + gcal_create_event: + description: "Create a calendar event. Schedule a meeting or appointment with attendees, time, location, and notifications." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + summary: + description: "Event title" + description: + description: "Event description (plain text or HTML)" + location: + description: "Event location" + start: + description: "Start time RFC3339 (e.g. '2024-03-15T14:00:00-07:00') OR YYYY-MM-DD for all-day" + end: + description: "End time RFC3339 OR YYYY-MM-DD for all-day (exclusive)" + time_zone: + description: "Time zone for start/end (e.g. 'America/Los_Angeles')" + attendees: + description: "Comma-separated attendee emails" + recurrence: + description: "Comma-separated RRULE/EXRULE/RDATE/EXDATE lines (e.g. 'RRULE:FREQ=WEEKLY;COUNT=10')" + send_updates: + description: "Send notifications: all, externalOnly, none (default none)" + conference_data_version: + description: "Set to '1' to enable conferenceData (e.g. auto-create Google Meet link via 'create_meet=true')" + create_meet: + description: "If true, attaches a Google Meet conference (requires conference_data_version=1)" + reminders_use_default: + description: "Use the calendar's default reminders (true/false)" + visibility: + description: "Visibility: default, public, private, confidential" + transparency: + description: "Show as: opaque (busy) or transparent (free)" + color_id: + description: "Color ID from gcal_get_colors event palette" + body: + description: "Raw JSON body — overrides all other fields. Use for advanced cases (attachments, custom reminders, extendedProperties)" + gcal_update_event: + description: "Replace a calendar event with PUT semantics — all fields must be provided. Prefer gcal_patch_event for partial updates." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + event_id: + description: "Event ID" + required: true + body: + description: "Full JSON event resource (replaces the entire event)" + required: true + send_updates: + description: "Send notifications: all, externalOnly, none (default none)" + gcal_patch_event: + description: "Partially update a calendar event. Edit a meeting's title, time, attendees, or other fields without replacing the entire event." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + event_id: + description: "Event ID" + required: true + summary: + description: "New title (optional)" + description: + description: "New description (optional)" + location: + description: "New location (optional)" + start: + description: "New start time RFC3339 or YYYY-MM-DD (optional)" + end: + description: "New end time RFC3339 or YYYY-MM-DD (optional)" + time_zone: + description: "Time zone for start/end" + attendees: + description: "Replace attendee list with comma-separated emails" + recurrence: + description: "Replace recurrence rules (comma-separated lines)" + send_updates: + description: "Send notifications: all, externalOnly, none (default none)" + visibility: + description: "Visibility: default, public, private, confidential" + transparency: + description: "Show as: opaque or transparent" + color_id: + description: "Color ID" + body: + description: "Raw JSON patch body — overrides other fields" + gcal_delete_event: + description: "Delete a calendar event. Cancel a meeting; optionally notify attendees." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + event_id: + description: "Event ID" + required: true + send_updates: + description: "Send cancellation notifications: all, externalOnly, none (default none)" + gcal_move_event: + description: "Move an event to a different calendar (re-home a meeting from personal to shared, for example)." + parameters: + calendar_id: + description: "Source calendar ID (defaults to 'primary')" + event_id: + description: "Event ID" + required: true + destination: + description: "Destination calendar ID" + required: true + send_updates: + description: "Send notifications: all, externalOnly, none (default none)" + gcal_list_instances: + description: "List the individual instances of a recurring event. Expand a weekly meeting into the actual occurrences." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + event_id: + description: "Recurring event ID" + required: true + time_min: + description: "Lower bound RFC3339" + time_max: + description: "Upper bound RFC3339" + max_results: + description: "Max results per page" + page_token: + description: "Token for next page" + show_deleted: + description: "Include cancelled instances (true/false)" + original_start: + description: "Original start time of a specific instance (RFC3339)" + time_zone: + description: "Time zone used in response" + gcal_quick_add_event: + description: "Create an event from a natural-language string (e.g. 'Lunch with Sarah Friday 1pm'). Quick way to schedule when you have a free-form text description." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + text: + description: "Natural-language event description" + required: true + send_updates: + description: "Send notifications: all, externalOnly, none (default none)" + gcal_import_event: + description: "Import an event from an external calendar (preserves the iCalUID, organizer, and original creation time). Use for syncing events; use create_event for new events." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + body: + description: "Full JSON event resource including iCalUID and organizer" + required: true + conference_data_version: + description: "Set to '1' to preserve conferenceData" + supports_attachments: + description: "Whether the API client supports event attachments (true/false)" + gcal_list_calendars: + description: "List the calendars in the user's calendar list. Discover which calendars are available to query (primary, secondary, shared, subscribed)." + parameters: + max_results: + description: "Max results per page (default 100, max 250)" + page_token: + description: "Token for next page" + show_deleted: + description: "Include deleted entries (true/false)" + show_hidden: + description: "Include hidden entries (true/false)" + min_access_role: + description: "Filter by minimum access role: freeBusyReader, reader, writer, owner" + gcal_get_calendar_subscription: + description: "Get a single calendar-list entry (the user-specific view of a subscribed calendar: color, hidden state, default reminders)." + parameters: + calendar_id: + description: "Calendar ID" + required: true + gcal_subscribe_calendar: + description: "Subscribe to an existing calendar by inserting it into the user's calendar list." + parameters: + calendar_id: + description: "Calendar ID to subscribe to" + required: true + color_rgb_format: + description: "Set to 'true' to use the color_id/background_color/foreground_color values" + background_color: + description: "Background color hex (e.g. '#ffffff')" + foreground_color: + description: "Foreground color hex" + color_id: + description: "Color ID from gcal_get_colors calendar palette" + hidden: + description: "Hide from UI (true/false)" + selected: + description: "Show in calendar view (true/false)" + summary_override: + description: "Custom display name for this user" + gcal_update_calendar_subscription: + description: "Update the user-specific properties of a calendar subscription (color, hidden state, summary override)." + parameters: + calendar_id: + description: "Calendar ID" + required: true + color_rgb_format: + description: "Set to 'true' to use the color hex values" + background_color: + description: "Background color hex" + foreground_color: + description: "Foreground color hex" + color_id: + description: "Color ID" + hidden: + description: "Hide from UI (true/false)" + selected: + description: "Show in calendar view (true/false)" + summary_override: + description: "Custom display name for this user" + body: + description: "Raw JSON body — overrides other fields" + gcal_unsubscribe_calendar: + description: "Remove a calendar from the user's calendar list (unsubscribe — does not delete the underlying calendar)." + parameters: + calendar_id: + description: "Calendar ID" + required: true + gcal_get_calendar: + description: "Get the metadata of a calendar resource (summary, description, time zone, location)." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + gcal_create_calendar: + description: "Create a new secondary calendar owned by the user." + parameters: + summary: + description: "Calendar title" + required: true + description: + description: "Calendar description" + location: + description: "Geographic location of the calendar" + time_zone: + description: "Time zone (e.g. 'America/Los_Angeles')" + body: + description: "Raw JSON body — overrides other fields" + gcal_update_calendar: + description: "Update a calendar's metadata (summary, description, time zone)." + parameters: + calendar_id: + description: "Calendar ID" + required: true + summary: + description: "New calendar title" + description: + description: "New description" + location: + description: "New location" + time_zone: + description: "New time zone" + body: + description: "Raw JSON patch body — overrides other fields" + gcal_delete_calendar: + description: "Permanently delete a secondary calendar (cannot delete primary calendars; use gcal_clear_calendar for primary)." + parameters: + calendar_id: + description: "Calendar ID" + required: true + gcal_clear_calendar: + description: "Clear all events from the user's primary calendar. Use only for the primary calendar; secondaries should be deleted instead." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + gcal_list_acl: + description: "List the access control rules (sharing) for a calendar." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + max_results: + description: "Max results per page" + page_token: + description: "Token for next page" + show_deleted: + description: "Include deleted ACL entries (true/false)" + gcal_get_acl: + description: "Get a single access control rule by ID." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + rule_id: + description: "ACL rule ID" + required: true + gcal_create_acl: + description: "Grant access to a calendar. Share with a specific user, group, or domain." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + role: + description: "Role: none, freeBusyReader, reader, writer, owner" + required: true + scope_type: + description: "Scope type: default, user, group, domain" + required: true + scope_value: + description: "Scope value (email for user/group, domain name for domain; omit for 'default')" + send_notifications: + description: "Send notification email (true/false)" + body: + description: "Raw JSON body — overrides other fields" + gcal_update_acl: + description: "Update a calendar access control rule (change the granted role)." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + rule_id: + description: "ACL rule ID" + required: true + role: + description: "New role: none, freeBusyReader, reader, writer, owner" + scope_type: + description: "Scope type (must match existing): default, user, group, domain" + scope_value: + description: "Scope value" + body: + description: "Raw JSON body — overrides other fields" + gcal_delete_acl: + description: "Revoke a calendar access control rule (unshare)." + parameters: + calendar_id: + description: "Calendar ID (defaults to 'primary')" + rule_id: + description: "ACL rule ID" + required: true + gcal_query_freebusy: + description: "Check availability across calendars in a time range. Returns busy intervals — use to find open time slots for scheduling meetings." + parameters: + time_min: + description: "Lower bound RFC3339" + required: true + time_max: + description: "Upper bound RFC3339" + required: true + items: + description: "Comma-separated calendar IDs (or group IDs) to query" + required: true + time_zone: + description: "Time zone for the response" + calendar_expansion_max: + description: "Max number of calendars to expand from groups (default 50)" + group_expansion_max: + description: "Max members per group expansion (default 100)" + body: + description: "Raw JSON body — overrides other fields" + gcal_list_settings: + description: "List the user's Calendar settings (week start, timezone, default event length, working hours)." + parameters: + max_results: + description: "Max results per page" + page_token: + description: "Token for next page" + gcal_get_setting: + description: "Get a single user Calendar setting by ID." + parameters: + setting_id: + description: "Setting ID (e.g. 'timezone', 'weekStart', 'defaultEventLength')" + required: true + gcal_get_colors: + description: "Get the color palettes available for calendars and events. Returns the mapping of color IDs to hex values." diff --git a/integrations/gchat/tools.go b/integrations/gchat/tools.go index 95a7b760..93f11342 100644 --- a/integrations/gchat/tools.go +++ b/integrations/gchat/tools.go @@ -1,89 +1,12 @@ package gchat -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Space resource ────────────────────────────────────────────── - { - Name: mcp.ToolName("gchat_list_spaces"), Description: "List Google Chat spaces the authenticated user belongs to. Start here for chat, conversations, messages, group chats, rooms, direct messages, DMs, threads, channels — to discover the space IDs needed by every other gchat tool. Returns spaces (full resource names like 'spaces/AAQAtuk0o-A'), their type (SPACE / GROUP_CHAT / DIRECT_MESSAGE), and display name (for named rooms).", - Parameters: map[string]string{ - "page_size": "Optional max spaces per page (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "filter": "Optional filter expression (e.g. 'space_type = \"SPACE\"' for named rooms only, 'space_type = \"DIRECT_MESSAGE\"' for DMs)", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gchat_get_space"), Description: "Retrieve a single Google Chat space by resource name. Returns the space's display name, type, threading state, and history state.", - Parameters: map[string]string{ - "space_id": "The space ID — accepts either bare ID ('AAQAtuk0o-A') or full resource name ('spaces/AAQAtuk0o-A')", - }, - Required: []string{"space_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Message resource ──────────────────────────────────────────── - { - Name: mcp.ToolName("gchat_list_messages"), Description: "List messages in a Google Chat space (rooms, group chats, DMs). Returns messages with sender, text, createTime, lastUpdateTime, and thread info. Use filter to narrow by createTime range; use order_by='createTime desc' to get newest-first.", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form)", - "page_size": "Optional max messages per page (1-1000, default 25)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "filter": "Optional filter expression (e.g. 'createTime > \"2024-01-01T00:00:00Z\"', 'thread.name = \"spaces/X/threads/Y\"')", - "order_by": "Optional ordering — 'createTime' (oldest first, default) or 'createTime desc' (newest first)", - "show_deleted": "Optional boolean — include deleted messages (default false)", - }, - Required: []string{"space_id"}, - }, - { - Name: mcp.ToolName("gchat_get_message"), Description: "Retrieve a single Google Chat message by space + message ID. Returns the message text, sender, createTime, lastUpdateTime, thread, attachments, and any cardsV2 payload.", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form)", - "message_id": "The message ID (the segment after 'messages/' in the message name — typically contains dots, e.g. 'UMOmwAAAAAE.UMOmwAAAAAE')", - }, - Required: []string{"space_id", "message_id"}, - }, - { - Name: mcp.ToolName("gchat_create_message"), Description: "Send a new message to a Google Chat space. Returns the created message including its resource name. Pass text for a plain-text message; pass cards_v2 (raw JSON) for a card message. Use thread_key with message_reply_option to reply in an existing thread.", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form) to send the message to", - "text": "Message text (plain text; supports basic formatting like *bold*, _italic_, `code`, and annotations)", - "cards_v2": "Optional raw JSON for cardsV2 payload (array of card objects per the Chat API spec). Use instead of or alongside text.", - "thread_key": "Optional client-supplied thread key — messages sharing the same key go in one thread (requires message_reply_option)", - "message_reply_option": "Optional reply behavior: 'REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD' (reply in thread; new thread if no match), 'REPLY_MESSAGE_OR_FAIL' (reply or fail)", - "message_id": "Optional custom message ID (alphanumeric, max 56 chars, must start with 'client-')", - }, - Required: []string{"space_id"}, - }, - { - Name: mcp.ToolName("gchat_update_message"), Description: "Update an existing Google Chat message (edit its text or replace its cardsV2 content). Uses PATCH semantics with an auto-generated updateMask — only the fields you pass are changed. Only messages sent by the authenticated user / app can be updated.", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form)", - "message_id": "The message ID", - "text": "Optional new message text", - "cards_v2": "Optional new cardsV2 payload (raw JSON array)", - }, - Required: []string{"space_id", "message_id"}, - }, - { - Name: mcp.ToolName("gchat_delete_message"), Description: "Delete a Google Chat message. Only messages sent by the authenticated user / app can be deleted. Set force=true to also delete any threaded replies when deleting the thread head.", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form)", - "message_id": "The message ID to delete", - "force": "Optional boolean — when deleting the head of a thread, also delete the threaded replies (default false)", - }, - Required: []string{"space_id", "message_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Membership resource ───────────────────────────────────────── - { - Name: mcp.ToolName("gchat_list_members"), Description: "List members (human users and chat apps) of a Google Chat space. Returns each member's name (resource id), role (ROLE_MEMBER / ROLE_MANAGER), state (JOINED / INVITED / NOT_A_MEMBER), and member type (HUMAN / BOT).", - Parameters: map[string]string{ - "space_id": "The space ID (bare or 'spaces/X' form)", - "page_size": "Optional max members per page (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "show_invited": "Optional boolean — include invited members (default false; only JOINED members otherwise)", - "filter": "Optional filter (e.g. 'member.type = \"HUMAN\"', 'role = \"ROLE_MANAGER\"')", - }, - Required: []string{"space_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gchat/tools.yaml b/integrations/gchat/tools.yaml new file mode 100644 index 00000000..f15b2cfa --- /dev/null +++ b/integrations/gchat/tools.yaml @@ -0,0 +1,96 @@ +version: 1 +tools: + gchat_list_spaces: + description: "List Google Chat spaces the authenticated user belongs to. Start here for chat, conversations, messages, group chats, rooms, direct messages, DMs, threads, channels — to discover the space IDs needed by every other gchat tool. Returns spaces (full resource names like 'spaces/AAQAtuk0o-A'), their type (SPACE / GROUP_CHAT / DIRECT_MESSAGE), and display name (for named rooms)." + parameters: + page_size: + description: "Optional max spaces per page (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + filter: + description: "Optional filter expression (e.g. 'space_type = \"SPACE\"' for named rooms only, 'space_type = \"DIRECT_MESSAGE\"' for DMs)" + gchat_get_space: + description: "Retrieve a single Google Chat space by resource name. Returns the space's display name, type, threading state, and history state." + parameters: + space_id: + description: "The space ID — accepts either bare ID ('AAQAtuk0o-A') or full resource name ('spaces/AAQAtuk0o-A')" + required: true + gchat_list_messages: + description: "List messages in a Google Chat space (rooms, group chats, DMs). Returns messages with sender, text, createTime, lastUpdateTime, and thread info. Use filter to narrow by createTime range; use order_by='createTime desc' to get newest-first." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form)" + required: true + page_size: + description: "Optional max messages per page (1-1000, default 25)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + filter: + description: "Optional filter expression (e.g. 'createTime > \"2024-01-01T00:00:00Z\"', 'thread.name = \"spaces/X/threads/Y\"')" + order_by: + description: "Optional ordering — 'createTime' (oldest first, default) or 'createTime desc' (newest first)" + show_deleted: + description: "Optional boolean — include deleted messages (default false)" + gchat_get_message: + description: "Retrieve a single Google Chat message by space + message ID. Returns the message text, sender, createTime, lastUpdateTime, thread, attachments, and any cardsV2 payload." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form)" + required: true + message_id: + description: "The message ID (the segment after 'messages/' in the message name — typically contains dots, e.g. 'UMOmwAAAAAE.UMOmwAAAAAE')" + required: true + gchat_create_message: + description: "Send a new message to a Google Chat space. Returns the created message including its resource name. Pass text for a plain-text message; pass cards_v2 (raw JSON) for a card message. Use thread_key with message_reply_option to reply in an existing thread." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form) to send the message to" + required: true + text: + description: "Message text (plain text; supports basic formatting like *bold*, _italic_, `code`, and annotations)" + cards_v2: + description: "Optional raw JSON for cardsV2 payload (array of card objects per the Chat API spec). Use instead of or alongside text." + thread_key: + description: "Optional client-supplied thread key — messages sharing the same key go in one thread (requires message_reply_option)" + message_reply_option: + description: "Optional reply behavior: 'REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD' (reply in thread; new thread if no match), 'REPLY_MESSAGE_OR_FAIL' (reply or fail)" + message_id: + description: "Optional custom message ID (alphanumeric, max 56 chars, must start with 'client-')" + gchat_update_message: + description: "Update an existing Google Chat message (edit its text or replace its cardsV2 content). Uses PATCH semantics with an auto-generated updateMask — only the fields you pass are changed. Only messages sent by the authenticated user / app can be updated." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form)" + required: true + message_id: + description: "The message ID" + required: true + text: + description: "Optional new message text" + cards_v2: + description: "Optional new cardsV2 payload (raw JSON array)" + gchat_delete_message: + description: "Delete a Google Chat message. Only messages sent by the authenticated user / app can be deleted. Set force=true to also delete any threaded replies when deleting the thread head." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form)" + required: true + message_id: + description: "The message ID to delete" + required: true + force: + description: "Optional boolean — when deleting the head of a thread, also delete the threaded replies (default false)" + gchat_list_members: + description: "List members (human users and chat apps) of a Google Chat space. Returns each member's name (resource id), role (ROLE_MEMBER / ROLE_MANAGER), state (JOINED / INVITED / NOT_A_MEMBER), and member type (HUMAN / BOT)." + parameters: + space_id: + description: "The space ID (bare or 'spaces/X' form)" + required: true + page_size: + description: "Optional max members per page (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + show_invited: + description: "Optional boolean — include invited members (default false; only JOINED members otherwise)" + filter: + description: "Optional filter (e.g. 'member.type = \"HUMAN\"', 'role = \"ROLE_MANAGER\"')" diff --git a/integrations/gcp/tools.go b/integrations/gcp/tools.go index 876464d1..639b620b 100644 --- a/integrations/gcp/tools.go +++ b/integrations/gcp/tools.go @@ -1,277 +1,12 @@ package gcp -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Resource Manager ───────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_get_project"), Description: "Get details about the configured GCP project. Start here to verify project access.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_list_projects"), Description: "Search for GCP projects the caller has access to", - Parameters: map[string]string{"query": "Filter query (e.g. name:my-project)"}, - }, - { - Name: mcp.ToolName("gcp_list_folders"), Description: "List folders under a parent resource", - Parameters: map[string]string{"parent": "Parent resource name (e.g. organizations/123)"}, - Required: []string{"parent"}, - }, - { - Name: mcp.ToolName("gcp_get_folder"), Description: "Get details about a folder", - Parameters: map[string]string{"folder_id": "Folder ID"}, - Required: []string{"folder_id"}, - }, - { - Name: mcp.ToolName("gcp_get_iam_policy"), Description: "Get IAM policy for the configured project", - Parameters: map[string]string{}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Cloud Storage ──────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_storage_list_buckets"), Description: "List all Cloud Storage buckets in the project", - Parameters: map[string]string{"limit": "Maximum number of buckets to return (default 1000)"}, - }, - { - Name: mcp.ToolName("gcp_storage_get_bucket"), Description: "Get metadata for a Cloud Storage bucket", - Parameters: map[string]string{"bucket": "Bucket name"}, - Required: []string{"bucket"}, - }, - { - Name: mcp.ToolName("gcp_storage_list_objects"), Description: "List objects in a Cloud Storage bucket (default limit 1000)", - Parameters: map[string]string{"bucket": "Bucket name", "prefix": "Object name prefix filter", "delimiter": "Delimiter for hierarchy (e.g. /)", "limit": "Maximum total results to return (default 1000)"}, - Required: []string{"bucket"}, - }, - { - Name: mcp.ToolName("gcp_storage_get_object"), Description: "Get an object from Cloud Storage (max 10MB; returns metadata and body as text for text types, base64 for binary)", - Parameters: map[string]string{"bucket": "Bucket name", "object": "Object name/path"}, - Required: []string{"bucket", "object"}, - }, - { - Name: mcp.ToolName("gcp_storage_put_object"), Description: "Upload an object to Cloud Storage", - Parameters: map[string]string{"bucket": "Bucket name", "object": "Object name/path", "body": "Object content (text)", "content_type": "MIME type (default: application/octet-stream)"}, - Required: []string{"bucket", "object", "body"}, - }, - { - Name: mcp.ToolName("gcp_storage_delete_object"), Description: "Delete an object from Cloud Storage", - Parameters: map[string]string{"bucket": "Bucket name", "object": "Object name/path"}, - Required: []string{"bucket", "object"}, - }, - { - Name: mcp.ToolName("gcp_storage_copy_object"), Description: "Copy an object within Cloud Storage", - Parameters: map[string]string{"source_bucket": "Source bucket name", "source_object": "Source object name", "dest_bucket": "Destination bucket name", "dest_object": "Destination object name"}, - Required: []string{"source_bucket", "source_object", "dest_bucket", "dest_object"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Compute Engine ─────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_compute_list_instances"), Description: "List Compute Engine VM instances (virtual machines) in a zone. View production server infrastructure. Default limit 500.", - Parameters: map[string]string{"zone": "Zone name (e.g. us-central1-a)", "filter": "Filter expression", "max_results": "Page size for API requests", "limit": "Maximum total results to return (default 500)"}, - Required: []string{"zone"}, - }, - { - Name: mcp.ToolName("gcp_compute_get_instance"), Description: "Get details for a specific Compute Engine instance", - Parameters: map[string]string{"zone": "Zone name", "instance": "Instance name"}, - Required: []string{"zone", "instance"}, - }, - { - Name: mcp.ToolName("gcp_compute_start_instance"), Description: "Start a Compute Engine instance", - Parameters: map[string]string{"zone": "Zone name", "instance": "Instance name"}, - Required: []string{"zone", "instance"}, - }, - { - Name: mcp.ToolName("gcp_compute_stop_instance"), Description: "Stop a Compute Engine instance", - Parameters: map[string]string{"zone": "Zone name", "instance": "Instance name"}, - Required: []string{"zone", "instance"}, - }, - { - Name: mcp.ToolName("gcp_compute_list_disks"), Description: "List persistent disks in a zone (default limit 500)", - Parameters: map[string]string{"zone": "Zone name", "filter": "Filter expression", "max_results": "Page size for API requests", "limit": "Maximum total results to return (default 500)"}, - Required: []string{"zone"}, - }, - { - Name: mcp.ToolName("gcp_compute_list_networks"), Description: "List VPC networks in the project (default limit 500)", - Parameters: map[string]string{"filter": "Filter expression", "max_results": "Page size for API requests", "limit": "Maximum total results to return (default 500)"}, - }, - { - Name: mcp.ToolName("gcp_compute_list_subnetworks"), Description: "List subnetworks in a region (default limit 500)", - Parameters: map[string]string{"region": "Region name (e.g. us-central1)", "filter": "Filter expression", "max_results": "Page size for API requests", "limit": "Maximum total results to return (default 500)"}, - Required: []string{"region"}, - }, - { - Name: mcp.ToolName("gcp_compute_list_firewalls"), Description: "List firewall rules in the project (default limit 500)", - Parameters: map[string]string{"filter": "Filter expression", "max_results": "Page size for API requests", "limit": "Maximum total results to return (default 500)"}, - }, - { - Name: mcp.ToolName("gcp_compute_get_firewall"), Description: "Get details for a specific firewall rule", - Parameters: map[string]string{"firewall": "Firewall rule name"}, - Required: []string{"firewall"}, - }, - - // ── Cloud Functions ────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_functions_list"), Description: "List Cloud Functions (serverless) in a location. View deployed functions and their configurations.", - Parameters: map[string]string{"location": "Location (e.g. us-central1, or - for all locations)"}, - }, - { - Name: mcp.ToolName("gcp_functions_get"), Description: "Get details about a Cloud Function", - Parameters: map[string]string{"name": "Full resource name (projects/PROJECT/locations/LOCATION/functions/NAME)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gcp_functions_get_iam_policy"), Description: "Get IAM policy for a Cloud Function", - Parameters: map[string]string{"name": "Full resource name of the function"}, - Required: []string{"name"}, - }, - - // ── IAM ────────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_iam_list_service_accounts"), Description: "List service accounts (automation identities) in the project. Review access credentials and security configuration.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_iam_get_service_account"), Description: "Get details about a service account", - Parameters: map[string]string{"email": "Service account email"}, - Required: []string{"email"}, - }, - { - Name: mcp.ToolName("gcp_iam_list_service_account_keys"), Description: "List keys for a service account", - Parameters: map[string]string{"email": "Service account email"}, - Required: []string{"email"}, - }, - { - Name: mcp.ToolName("gcp_iam_list_roles"), Description: "List predefined and custom IAM roles", - Parameters: map[string]string{"show_deleted": "Include deleted roles (true/false)"}, - }, - { - Name: mcp.ToolName("gcp_iam_get_role"), Description: "Get details about an IAM role", - Parameters: map[string]string{"name": "Role name (e.g. roles/editor or projects/PROJECT/roles/CUSTOM)"}, - Required: []string{"name"}, - }, - - // ── Cloud Monitoring ───────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_monitoring_list_metric_descriptors"), Description: "List available metric descriptors", - Parameters: map[string]string{"filter": "Metric filter (e.g. metric.type = starts_with(\"compute.googleapis.com\"))"}, - }, - { - Name: mcp.ToolName("gcp_monitoring_list_time_series"), Description: "Get Cloud Monitoring time series data for a metric. Query production performance, CPU, memory, and custom metric graphs.", - Parameters: map[string]string{"filter": "Time series filter (e.g. metric.type=\"compute.googleapis.com/instance/cpu/utilization\")", "start_time": "Start time (RFC3339)", "end_time": "End time (RFC3339, default now)", "alignment_period": "Alignment period in seconds (e.g. 60s)", "per_series_aligner": "Aligner: ALIGN_MEAN, ALIGN_SUM, ALIGN_MAX, ALIGN_MIN, etc."}, - Required: []string{"filter", "start_time"}, - }, - { - Name: mcp.ToolName("gcp_monitoring_list_alert_policies"), Description: "List Cloud Monitoring alert policies for production threshold warnings and notifications", - Parameters: map[string]string{"filter": "Filter expression"}, - }, - { - Name: mcp.ToolName("gcp_monitoring_get_alert_policy"), Description: "Get details about an alert policy", - Parameters: map[string]string{"name": "Full resource name of the alert policy"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gcp_monitoring_list_monitored_resources"), Description: "List monitored resource descriptors", - Parameters: map[string]string{"filter": "Filter expression"}, - }, - - // ── Cloud Run ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_run_list_services"), Description: "List Cloud Run serverless container services in a location. View production deployments and configurations.", - Parameters: map[string]string{"location": "Location (e.g. us-central1)"}, - Required: []string{"location"}, - }, - { - Name: mcp.ToolName("gcp_run_get_service"), Description: "Get details about a Cloud Run service", - Parameters: map[string]string{"name": "Full resource name (projects/PROJECT/locations/LOCATION/services/NAME)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gcp_run_list_revisions"), Description: "List revisions of a Cloud Run service", - Parameters: map[string]string{"service_name": "Full resource name of the parent service"}, - Required: []string{"service_name"}, - }, - { - Name: mcp.ToolName("gcp_run_get_revision"), Description: "Get details about a Cloud Run revision", - Parameters: map[string]string{"name": "Full resource name of the revision"}, - Required: []string{"name"}, - }, - - // ── Pub/Sub ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_pubsub_list_topics"), Description: "List Pub/Sub topics in the project", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_pubsub_get_topic"), Description: "Get configuration for a Pub/Sub topic", - Parameters: map[string]string{"topic": "Topic ID"}, - Required: []string{"topic"}, - }, - { - Name: mcp.ToolName("gcp_pubsub_publish"), Description: "Publish a message to a Pub/Sub topic", - Parameters: map[string]string{"topic": "Topic ID", "message": "Message data (text)", "attributes": "JSON object of message attributes"}, - Required: []string{"topic", "message"}, - }, - { - Name: mcp.ToolName("gcp_pubsub_list_subscriptions"), Description: "List Pub/Sub subscriptions in the project", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_pubsub_get_subscription"), Description: "Get configuration for a Pub/Sub subscription", - Parameters: map[string]string{"subscription": "Subscription ID"}, - Required: []string{"subscription"}, - }, - { - Name: mcp.ToolName("gcp_pubsub_pull"), Description: "Pull messages from a Pub/Sub subscription", - Parameters: map[string]string{"subscription": "Subscription ID", "max_messages": "Maximum messages to pull (default 10)", "timeout": "Timeout in seconds to wait for messages (default 10)"}, - Required: []string{"subscription"}, - }, - - // ── Firestore ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_firestore_list_collections"), Description: "List top-level collections in Firestore", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_firestore_list_documents"), Description: "List documents in a Firestore collection", - Parameters: map[string]string{"collection": "Collection path", "limit": "Maximum number of documents to return"}, - Required: []string{"collection"}, - }, - { - Name: mcp.ToolName("gcp_firestore_get_document"), Description: "Get a document from Firestore", - Parameters: map[string]string{"path": "Document path (e.g. collection/docId)"}, - Required: []string{"path"}, - }, - { - Name: mcp.ToolName("gcp_firestore_set_document"), Description: "Create or update a document in Firestore", - Parameters: map[string]string{"path": "Document path (e.g. collection/docId)", "data": "JSON object with document fields"}, - Required: []string{"path", "data"}, - }, - { - Name: mcp.ToolName("gcp_firestore_delete_document"), Description: "Delete a document from Firestore", - Parameters: map[string]string{"path": "Document path (e.g. collection/docId)"}, - Required: []string{"path"}, - }, - { - Name: mcp.ToolName("gcp_firestore_query"), Description: "Query documents in a Firestore collection", - Parameters: map[string]string{"collection": "Collection path", "where_field": "Field to filter on", "where_op": "Operator: ==, !=, <, <=, >, >=, array-contains, in", "where_value": "Value to compare (JSON-encoded: use 123 for numbers, \"\\\"text\\\"\" for strings, true/false for booleans)", "order_by": "Field to order by", "order_dir": "Order direction: asc or desc", "limit": "Maximum number of documents"}, - Required: []string{"collection"}, - }, - - // ── Cloud Logging ──────────────────────────────────────────────── - { - Name: mcp.ToolName("gcp_logging_list_entries"), Description: "List Cloud Logging entries for the project. Search production logs for errors, debugging, and observability.", - Parameters: map[string]string{"filter": "Logging filter (e.g. severity>=ERROR)", "order_by": "Order: timestamp asc or timestamp desc (default desc)", "page_size": "Maximum entries to return (default 50)"}, - }, - { - Name: mcp.ToolName("gcp_logging_list_log_names"), Description: "List log names in the project", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_logging_list_sinks"), Description: "List logging sinks in the project", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("gcp_logging_get_sink"), Description: "Get details about a logging sink", - Parameters: map[string]string{"sink_name": "Sink name"}, - Required: []string{"sink_name"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gcp/tools.yaml b/integrations/gcp/tools.yaml new file mode 100644 index 00000000..8c41ac72 --- /dev/null +++ b/integrations/gcp/tools.yaml @@ -0,0 +1,448 @@ +version: 1 +tools: + gcp_get_project: + description: "Get details about the configured GCP project. Start here to verify project access." + parameters: {} + + gcp_list_projects: + description: "Search for GCP projects the caller has access to" + parameters: + query: + description: "Filter query (e.g. name:my-project)" + + gcp_list_folders: + description: "List folders under a parent resource" + parameters: + parent: + description: "Parent resource name (e.g. organizations/123)" + required: true + + gcp_get_folder: + description: "Get details about a folder" + parameters: + folder_id: + description: "Folder ID" + required: true + + gcp_get_iam_policy: + description: "Get IAM policy for the configured project" + parameters: {} + + gcp_storage_list_buckets: + description: "List all Cloud Storage buckets in the project" + parameters: + limit: + description: "Maximum number of buckets to return (default 1000)" + + gcp_storage_get_bucket: + description: "Get metadata for a Cloud Storage bucket" + parameters: + bucket: + description: "Bucket name" + required: true + + gcp_storage_list_objects: + description: "List objects in a Cloud Storage bucket (default limit 1000)" + parameters: + bucket: + description: "Bucket name" + required: true + prefix: + description: "Object name prefix filter" + delimiter: + description: "Delimiter for hierarchy (e.g. /)" + limit: + description: "Maximum total results to return (default 1000)" + + gcp_storage_get_object: + description: "Get an object from Cloud Storage (max 10MB; returns metadata and body as text for text types, base64 for binary)" + parameters: + bucket: + description: "Bucket name" + required: true + object: + description: "Object name/path" + required: true + + gcp_storage_put_object: + description: "Upload an object to Cloud Storage" + parameters: + bucket: + description: "Bucket name" + required: true + object: + description: "Object name/path" + required: true + body: + description: "Object content (text)" + required: true + content_type: + description: "MIME type (default: application/octet-stream)" + + gcp_storage_delete_object: + description: "Delete an object from Cloud Storage" + parameters: + bucket: + description: "Bucket name" + required: true + object: + description: "Object name/path" + required: true + + gcp_storage_copy_object: + description: "Copy an object within Cloud Storage" + parameters: + source_bucket: + description: "Source bucket name" + required: true + source_object: + description: "Source object name" + required: true + dest_bucket: + description: "Destination bucket name" + required: true + dest_object: + description: "Destination object name" + required: true + + gcp_compute_list_instances: + description: "List Compute Engine VM instances (virtual machines) in a zone. View production server infrastructure. Default limit 500." + parameters: + zone: + description: "Zone name (e.g. us-central1-a)" + required: true + filter: + description: "Filter expression" + max_results: + description: "Page size for API requests" + limit: + description: "Maximum total results to return (default 500)" + + gcp_compute_get_instance: + description: "Get details for a specific Compute Engine instance" + parameters: + zone: + description: "Zone name" + required: true + instance: + description: "Instance name" + required: true + + gcp_compute_start_instance: + description: "Start a Compute Engine instance" + parameters: + zone: + description: "Zone name" + required: true + instance: + description: "Instance name" + required: true + + gcp_compute_stop_instance: + description: "Stop a Compute Engine instance" + parameters: + zone: + description: "Zone name" + required: true + instance: + description: "Instance name" + required: true + + gcp_compute_list_disks: + description: "List persistent disks in a zone (default limit 500)" + parameters: + zone: + description: "Zone name" + required: true + filter: + description: "Filter expression" + max_results: + description: "Page size for API requests" + limit: + description: "Maximum total results to return (default 500)" + + gcp_compute_list_networks: + description: "List VPC networks in the project (default limit 500)" + parameters: + filter: + description: "Filter expression" + max_results: + description: "Page size for API requests" + limit: + description: "Maximum total results to return (default 500)" + + gcp_compute_list_subnetworks: + description: "List subnetworks in a region (default limit 500)" + parameters: + region: + description: "Region name (e.g. us-central1)" + required: true + filter: + description: "Filter expression" + max_results: + description: "Page size for API requests" + limit: + description: "Maximum total results to return (default 500)" + + gcp_compute_list_firewalls: + description: "List firewall rules in the project (default limit 500)" + parameters: + filter: + description: "Filter expression" + max_results: + description: "Page size for API requests" + limit: + description: "Maximum total results to return (default 500)" + + gcp_compute_get_firewall: + description: "Get details for a specific firewall rule" + parameters: + firewall: + description: "Firewall rule name" + required: true + + gcp_functions_list: + description: "List Cloud Functions (serverless) in a location. View deployed functions and their configurations." + parameters: + location: + description: "Location (e.g. us-central1, or - for all locations)" + + gcp_functions_get: + description: "Get details about a Cloud Function" + parameters: + name: + description: "Full resource name (projects/PROJECT/locations/LOCATION/functions/NAME)" + required: true + + gcp_functions_get_iam_policy: + description: "Get IAM policy for a Cloud Function" + parameters: + name: + description: "Full resource name of the function" + required: true + + gcp_iam_list_service_accounts: + description: "List service accounts (automation identities) in the project. Review access credentials and security configuration." + parameters: {} + + gcp_iam_get_service_account: + description: "Get details about a service account" + parameters: + email: + description: "Service account email" + required: true + + gcp_iam_list_service_account_keys: + description: "List keys for a service account" + parameters: + email: + description: "Service account email" + required: true + + gcp_iam_list_roles: + description: "List predefined and custom IAM roles" + parameters: + show_deleted: + description: "Include deleted roles (true/false)" + + gcp_iam_get_role: + description: "Get details about an IAM role" + parameters: + name: + description: "Role name (e.g. roles/editor or projects/PROJECT/roles/CUSTOM)" + required: true + + gcp_monitoring_list_metric_descriptors: + description: "List available metric descriptors" + parameters: + filter: + description: 'Metric filter (e.g. metric.type = starts_with("compute.googleapis.com"))' + + gcp_monitoring_list_time_series: + description: "Get Cloud Monitoring time series data for a metric. Query production performance, CPU, memory, and custom metric graphs." + parameters: + filter: + description: 'Time series filter (e.g. metric.type="compute.googleapis.com/instance/cpu/utilization")' + required: true + start_time: + description: "Start time (RFC3339)" + required: true + end_time: + description: "End time (RFC3339, default now)" + alignment_period: + description: "Alignment period in seconds (e.g. 60s)" + per_series_aligner: + description: "Aligner: ALIGN_MEAN, ALIGN_SUM, ALIGN_MAX, ALIGN_MIN, etc." + + gcp_monitoring_list_alert_policies: + description: "List Cloud Monitoring alert policies for production threshold warnings and notifications" + parameters: + filter: + description: "Filter expression" + + gcp_monitoring_get_alert_policy: + description: "Get details about an alert policy" + parameters: + name: + description: "Full resource name of the alert policy" + required: true + + gcp_monitoring_list_monitored_resources: + description: "List monitored resource descriptors" + parameters: + filter: + description: "Filter expression" + + gcp_run_list_services: + description: "List Cloud Run serverless container services in a location. View production deployments and configurations." + parameters: + location: + description: "Location (e.g. us-central1)" + required: true + + gcp_run_get_service: + description: "Get details about a Cloud Run service" + parameters: + name: + description: "Full resource name (projects/PROJECT/locations/LOCATION/services/NAME)" + required: true + + gcp_run_list_revisions: + description: "List revisions of a Cloud Run service" + parameters: + service_name: + description: "Full resource name of the parent service" + required: true + + gcp_run_get_revision: + description: "Get details about a Cloud Run revision" + parameters: + name: + description: "Full resource name of the revision" + required: true + + gcp_pubsub_list_topics: + description: "List Pub/Sub topics in the project" + parameters: {} + + gcp_pubsub_get_topic: + description: "Get configuration for a Pub/Sub topic" + parameters: + topic: + description: "Topic ID" + required: true + + gcp_pubsub_publish: + description: "Publish a message to a Pub/Sub topic" + parameters: + topic: + description: "Topic ID" + required: true + message: + description: "Message data (text)" + required: true + attributes: + description: "JSON object of message attributes" + + gcp_pubsub_list_subscriptions: + description: "List Pub/Sub subscriptions in the project" + parameters: {} + + gcp_pubsub_get_subscription: + description: "Get configuration for a Pub/Sub subscription" + parameters: + subscription: + description: "Subscription ID" + required: true + + gcp_pubsub_pull: + description: "Pull messages from a Pub/Sub subscription" + parameters: + subscription: + description: "Subscription ID" + required: true + max_messages: + description: "Maximum messages to pull (default 10)" + timeout: + description: "Timeout in seconds to wait for messages (default 10)" + + gcp_firestore_list_collections: + description: "List top-level collections in Firestore" + parameters: {} + + gcp_firestore_list_documents: + description: "List documents in a Firestore collection" + parameters: + collection: + description: "Collection path" + required: true + limit: + description: "Maximum number of documents to return" + + gcp_firestore_get_document: + description: "Get a document from Firestore" + parameters: + path: + description: "Document path (e.g. collection/docId)" + required: true + + gcp_firestore_set_document: + description: "Create or update a document in Firestore" + parameters: + path: + description: "Document path (e.g. collection/docId)" + required: true + data: + description: "JSON object with document fields" + required: true + + gcp_firestore_delete_document: + description: "Delete a document from Firestore" + parameters: + path: + description: "Document path (e.g. collection/docId)" + required: true + + gcp_firestore_query: + description: "Query documents in a Firestore collection" + parameters: + collection: + description: "Collection path" + required: true + where_field: + description: "Field to filter on" + where_op: + description: "Operator: ==, !=, <, <=, >, >=, array-contains, in" + where_value: + description: 'Value to compare (JSON-encoded: use 123 for numbers, "\"text\"" for strings, true/false for booleans)' + order_by: + description: "Field to order by" + order_dir: + description: "Order direction: asc or desc" + limit: + description: "Maximum number of documents" + + gcp_logging_list_entries: + description: "List Cloud Logging entries for the project. Search production logs for errors, debugging, and observability." + parameters: + filter: + description: "Logging filter (e.g. severity>=ERROR)" + order_by: + description: "Order: timestamp asc or timestamp desc (default desc)" + page_size: + description: "Maximum entries to return (default 50)" + + gcp_logging_list_log_names: + description: "List log names in the project" + parameters: {} + + gcp_logging_list_sinks: + description: "List logging sinks in the project" + parameters: {} + + gcp_logging_get_sink: + description: "Get details about a logging sink" + parameters: + sink_name: + description: "Sink name" + required: true diff --git a/integrations/gdocs/tools.go b/integrations/gdocs/tools.go index 00f7a73e..8364af96 100644 --- a/integrations/gdocs/tools.go +++ b/integrations/gdocs/tools.go @@ -1,71 +1,12 @@ package gdocs -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Document lifecycle ────────────────────────────────────────── - { - Name: mcp.ToolName("gdocs_get_document"), Description: "Retrieve (get) a Google Docs document by ID, including its full structured content (paragraphs, tables, lists, headings, images, footnotes). Start here for reading document text, getting word counts, finding sections, or fetching raw structure for analysis. Returns the document body as markdown via the renderer.", - Parameters: map[string]string{ - "document_id": "The Google Docs document ID (the long string in the document URL)", - "suggestions_view_mode": "How suggested edits are surfaced: DEFAULT_FOR_CURRENT_ACCESS, SUGGESTIONS_INLINE, PREVIEW_SUGGESTIONS_ACCEPTED, PREVIEW_WITHOUT_SUGGESTIONS", - "include_tabs_content": "true/false — include content of all tabs (default false; the API returns the active tab only otherwise)", - }, - Required: []string{"document_id"}, - }, - { - Name: mcp.ToolName("gdocs_create_document"), Description: "Create a new, empty Google Docs document. Returns the new document including its ID, which you can then pass to gdocs_batch_update, gdocs_insert_text, or gdocs_append_text to add content. To save the document into a specific Drive folder, use gdrive_update_file afterward to set parents.", - Parameters: map[string]string{ - "title": "Document title (defaults to 'Untitled document')", - }, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Edits: power + convenience ────────────────────────────────── - { - Name: mcp.ToolName("gdocs_batch_update"), Description: "Apply a batch of edit requests to a Google Docs document — the full power of the Docs API. Use after gdocs_get_document to see existing indices. Each request is one of: insertText, deleteContentRange, replaceAllText, insertTableRow, insertPageBreak, updateTextStyle, updateParagraphStyle, createParagraphBullets, deleteParagraphBullets, insertInlineImage, and many more. Prefer the convenience tools (gdocs_insert_text, gdocs_append_text, gdocs_replace_text, gdocs_delete_content) for simple edits; use this for compound or formatting changes.", - Parameters: map[string]string{ - "document_id": "The document ID to edit", - "requests": "JSON array of Docs API Request objects (e.g. [{\"insertText\":{\"location\":{\"index\":1},\"text\":\"Hello\"}}])", - "write_control_revision": "Optional required_revision_id — only apply if the document is at this revision", - "write_control_target": "Optional target_revision_id — only apply if the document is at or below this revision (use revision_id from gdocs_get_document)", - }, - Required: []string{"document_id", "requests"}, - }, - { - Name: mcp.ToolName("gdocs_insert_text"), Description: "Insert plain text at a specific character index in a Google Docs document. Convenience wrapper over gdocs_batch_update for the most common edit. Use gdocs_get_document first to find the index; index 1 is the start of the document body (index 0 is reserved).", - Parameters: map[string]string{ - "document_id": "The document ID", - "text": "The text to insert (may contain newlines for paragraph breaks)", - "index": "Insertion index (1-based; 1 = start of body). Use gdocs_append_text to insert at end.", - }, - Required: []string{"document_id", "text", "index"}, - }, - { - Name: mcp.ToolName("gdocs_append_text"), Description: "Append text to the end of a Google Docs document. Convenience wrapper that fetches the doc, computes the end index, and inserts. The simplest way to add content without needing to track indices.", - Parameters: map[string]string{ - "document_id": "The document ID", - "text": "The text to append", - "leading_newline": "true/false — prepend a newline before the appended text (default true for clean separation)", - }, - Required: []string{"document_id", "text"}, - }, - { - Name: mcp.ToolName("gdocs_replace_text"), Description: "Find and replace all occurrences of a string in a Google Docs document. Convenience wrapper over the Docs API's replaceAllText request. Use match_case=true for case-sensitive replacement.", - Parameters: map[string]string{ - "document_id": "The document ID", - "find": "Text to find", - "replace": "Replacement text (use empty string to delete all matches)", - "match_case": "true/false — case-sensitive matching (default false)", - }, - Required: []string{"document_id", "find", "replace"}, - }, - { - Name: mcp.ToolName("gdocs_delete_content"), Description: "Delete a range of content (text, images, tables) between two character indices in a Google Docs document. Convenience wrapper over the Docs API's deleteContentRange request. Use gdocs_get_document to find the indices first.", - Parameters: map[string]string{ - "document_id": "The document ID", - "start_index": "Start of range to delete (inclusive)", - "end_index": "End of range to delete (exclusive)", - }, - Required: []string{"document_id", "start_index", "end_index"}, - }, -} +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gdocs/tools.yaml b/integrations/gdocs/tools.yaml new file mode 100644 index 00000000..886e380d --- /dev/null +++ b/integrations/gdocs/tools.yaml @@ -0,0 +1,79 @@ +version: 1 +tools: + gdocs_get_document: + description: "Retrieve (get) a Google Docs document by ID, including its full structured content (paragraphs, tables, lists, headings, images, footnotes). Start here for reading document text, getting word counts, finding sections, or fetching raw structure for analysis. Returns the document body as markdown via the renderer." + parameters: + document_id: + description: "The Google Docs document ID (the long string in the document URL)" + required: true + suggestions_view_mode: + description: "How suggested edits are surfaced: DEFAULT_FOR_CURRENT_ACCESS, SUGGESTIONS_INLINE, PREVIEW_SUGGESTIONS_ACCEPTED, PREVIEW_WITHOUT_SUGGESTIONS" + include_tabs_content: + description: "true/false — include content of all tabs (default false; the API returns the active tab only otherwise)" + gdocs_create_document: + description: "Create a new, empty Google Docs document. Returns the new document including its ID, which you can then pass to gdocs_batch_update, gdocs_insert_text, or gdocs_append_text to add content. To save the document into a specific Drive folder, use gdrive_update_file afterward to set parents." + parameters: + title: + description: "Document title (defaults to 'Untitled document')" + gdocs_batch_update: + description: "Apply a batch of edit requests to a Google Docs document — the full power of the Docs API. Use after gdocs_get_document to see existing indices. Each request is one of: insertText, deleteContentRange, replaceAllText, insertTableRow, insertPageBreak, updateTextStyle, updateParagraphStyle, createParagraphBullets, deleteParagraphBullets, insertInlineImage, and many more. Prefer the convenience tools (gdocs_insert_text, gdocs_append_text, gdocs_replace_text, gdocs_delete_content) for simple edits; use this for compound or formatting changes." + parameters: + document_id: + description: "The document ID to edit" + required: true + requests: + description: 'JSON array of Docs API Request objects (e.g. [{"insertText":{"location":{"index":1},"text":"Hello"}}])' + required: true + write_control_revision: + description: "Optional required_revision_id — only apply if the document is at this revision" + write_control_target: + description: "Optional target_revision_id — only apply if the document is at or below this revision (use revision_id from gdocs_get_document)" + gdocs_insert_text: + description: "Insert plain text at a specific character index in a Google Docs document. Convenience wrapper over gdocs_batch_update for the most common edit. Use gdocs_get_document first to find the index; index 1 is the start of the document body (index 0 is reserved)." + parameters: + document_id: + description: "The document ID" + required: true + text: + description: "The text to insert (may contain newlines for paragraph breaks)" + required: true + index: + description: "Insertion index (1-based; 1 = start of body). Use gdocs_append_text to insert at end." + required: true + gdocs_append_text: + description: "Append text to the end of a Google Docs document. Convenience wrapper that fetches the doc, computes the end index, and inserts. The simplest way to add content without needing to track indices." + parameters: + document_id: + description: "The document ID" + required: true + text: + description: "The text to append" + required: true + leading_newline: + description: "true/false — prepend a newline before the appended text (default true for clean separation)" + gdocs_replace_text: + description: "Find and replace all occurrences of a string in a Google Docs document. Convenience wrapper over the Docs API's replaceAllText request. Use match_case=true for case-sensitive replacement." + parameters: + document_id: + description: "The document ID" + required: true + find: + description: "Text to find" + required: true + replace: + description: "Replacement text (use empty string to delete all matches)" + required: true + match_case: + description: "true/false — case-sensitive matching (default false)" + gdocs_delete_content: + description: "Delete a range of content (text, images, tables) between two character indices in a Google Docs document. Convenience wrapper over the Docs API's deleteContentRange request. Use gdocs_get_document to find the indices first." + parameters: + document_id: + description: "The document ID" + required: true + start_index: + description: "Start of range to delete (inclusive)" + required: true + end_index: + description: "End of range to delete (exclusive)" + required: true diff --git a/integrations/gdrive/tools.go b/integrations/gdrive/tools.go index e7037a7b..a7b210c8 100644 --- a/integrations/gdrive/tools.go +++ b/integrations/gdrive/tools.go @@ -1,383 +1,12 @@ package gdrive -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Files: search & read ──────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_list_files"), Description: "Search and list files and folders in Drive. Start here for finding documents, photos, folders, or anything else by name, owner, or type. Uses Drive's expressive query syntax (e.g. \"name contains 'budget'\", \"mimeType='application/vnd.google-apps.folder'\").", - Parameters: map[string]string{ - "q": "Drive search query (e.g. \"name contains 'report'\", \"'' in parents\", \"mimeType='application/vnd.google-apps.document'\", \"trashed=false\"). See https://developers.google.com/drive/api/guides/search-files", - "page_size": "Max results per page (default 100, max 1000)", - "page_token": "Token for next page", - "order_by": "Comma-separated sort keys with optional 'desc' suffix: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime", - "fields": "Selector for response fields (e.g. 'nextPageToken,files(id,name,mimeType,parents)'). Defaults to a useful subset", - "corpora": "Bodies of items to query: user, drive, allDrives, domain", - "drive_id": "ID of the shared drive to search (required when corpora=drive)", - "include_items_from_all_drives": "Include items from shared drives (true/false, requires supports_all_drives=true)", - "spaces": "Comma-separated spaces: drive, appDataFolder", - "supports_all_drives": "true/false (default true)", - }, - }, - { - Name: mcp.ToolName("gdrive_get_file"), Description: "Get metadata for a single file or folder (name, mimeType, size, parents, modifiedTime, owners, permissions, etc.). Use this for everything except the file content itself — for content use gdrive_download_file or gdrive_export_file.", - Parameters: map[string]string{ - "file_id": "File or folder ID", - "fields": "Field selector (default '*' returns all). Example: 'id,name,mimeType,parents,size,modifiedTime'", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_download_file"), Description: "Download the binary content of a non-Google file (PDFs, images, ZIPs, CSV, etc.). For Google Docs/Sheets/Slides use gdrive_export_file. Response is a JSON envelope with base64-encoded content plus content_type, or text content inline when content_type is text/*.", - Parameters: map[string]string{ - "file_id": "File ID", - "acknowledge_abuse": "Set to true to download a file flagged as malicious", - "supports_all_drives": "true/false (default true)", - "max_bytes": "Cap downloaded bytes (default 5_000_000, hard max 10_000_000)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_export_file"), Description: "Export a Google Docs Editors file (Docs, Sheets, Slides, Drawings) as a different MIME type. Common export types: 'application/pdf', 'text/plain', 'text/csv', 'text/markdown' (for Docs), 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' (.docx). Response is a JSON envelope with content_type plus either base64 content or inline text when content_type is text/*.", - Parameters: map[string]string{ - "file_id": "Google Docs/Sheets/Slides/Drawings file ID", - "mime_type": "Target MIME type to export as", - "max_bytes": "Cap exported bytes (default 5_000_000, hard max 10_000_000)", - }, - Required: []string{"file_id", "mime_type"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Files: write ──────────────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_create_file"), Description: "Create a new file or upload content. Two modes: (1) metadata-only — pass name/mime_type/parents; (2) with content — pass content (UTF-8 string) or content_base64 (binary). Use gdrive_create_folder for folders specifically.", - Parameters: map[string]string{ - "name": "File name", - "mime_type": "MIME type (e.g. 'text/plain', 'application/pdf', 'application/vnd.google-apps.document' to create a new Google Doc from plain text content)", - "parents": "Comma-separated parent folder IDs (omit for My Drive root)", - "description": "File description", - "content": "Text content (mutually exclusive with content_base64)", - "content_base64": "Base64-encoded binary content (mutually exclusive with content)", - "starred": "true/false", - "app_properties": "JSON object string for appProperties", - "properties": "JSON object string for properties", - "body": "Raw JSON file metadata body — overrides convenience args except content/content_base64", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gdrive_update_file"), Description: "Update an existing file's metadata and/or content. To rename, change parents, or update content. Use gdrive_trash_file for soft delete.", - Parameters: map[string]string{ - "file_id": "File ID", - "name": "New name", - "mime_type": "New MIME type", - "description": "New description", - "starred": "true/false", - "trashed": "true/false (prefer gdrive_trash_file)", - "add_parents": "Comma-separated parent IDs to add", - "remove_parents": "Comma-separated parent IDs to remove", - "content": "Text content to overwrite the file with", - "content_base64": "Base64-encoded binary content to overwrite the file with", - "body": "Raw JSON file metadata body — overrides convenience args", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_copy_file"), Description: "Copy a file to a new file (metadata + content). Note: cannot copy folders (use gdrive_list_files + repeated copies).", - Parameters: map[string]string{ - "file_id": "Source file ID", - "name": "New file name (defaults to 'Copy of ')", - "parents": "Comma-separated parent folder IDs", - "description": "New description", - "body": "Raw JSON metadata body — overrides convenience args", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_delete_file"), Description: "Permanently delete a file (no trash). Cannot be undone. For soft delete use gdrive_trash_file.", - Parameters: map[string]string{ - "file_id": "File ID", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_trash_file"), Description: "Move a file to the trash (soft delete, reversible via gdrive_untrash_file).", - Parameters: map[string]string{ - "file_id": "File ID", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_untrash_file"), Description: "Restore a file from the trash.", - Parameters: map[string]string{ - "file_id": "File ID", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_empty_trash"), Description: "Permanently delete all files in the user's trash. Cannot be undone.", - Parameters: map[string]string{ - "drive_id": "Shared drive ID (omit to empty My Drive trash)", - }, - }, - { - Name: mcp.ToolName("gdrive_create_folder"), Description: "Create a new folder. Convenience wrapper around gdrive_create_file with mime_type set to application/vnd.google-apps.folder.", - Parameters: map[string]string{ - "name": "Folder name", - "parents": "Comma-separated parent folder IDs (omit for My Drive root)", - "description": "Folder description", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gdrive_generate_ids"), Description: "Generate a set of file IDs that can be used in subsequent insert/copy requests. Useful for pre-allocating IDs before uploading.", - Parameters: map[string]string{ - "count": "Number of IDs to generate (default 10, max 1000)", - "space": "Space the IDs are for: drive (default) or appDataFolder", - "type": "Resource type the IDs are for: files (default) or shortcuts", - }, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Permissions (sharing) ─────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_list_permissions"), Description: "List the sharing permissions on a file or shared drive. Shows who has access at what role.", - Parameters: map[string]string{ - "file_id": "File or shared drive ID", - "page_size": "Max results per page", - "page_token": "Token for next page", - "supports_all_drives": "true/false (default true)", - "use_domain_admin_access": "true/false — issue as domain administrator", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_get_permission"), Description: "Get a specific permission entry by ID.", - Parameters: map[string]string{ - "file_id": "File or shared drive ID", - "permission_id": "Permission ID", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id", "permission_id"}, - }, - { - Name: mcp.ToolName("gdrive_create_permission"), Description: "Share a file or folder. Grants a role to a user, group, domain, or anyone. By default sends an email notification.", - Parameters: map[string]string{ - "file_id": "File or shared drive ID", - "role": "Role: owner, organizer, fileOrganizer, writer, commenter, reader", - "type": "Grantee type: user, group, domain, anyone", - "email_address": "Email (required for user/group)", - "domain": "Domain (required for type=domain)", - "allow_file_discovery": "true/false (for type=domain or anyone)", - "send_notification_email": "true/false (default true for user/group)", - "email_message": "Custom message to include in the notification email", - "transfer_ownership": "true/false — required when changing owner", - "move_to_new_owners_root": "true/false — when transferring ownership in My Drive", - "supports_all_drives": "true/false (default true)", - "body": "Raw JSON permission body — overrides convenience args", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_update_permission"), Description: "Update a permission's role.", - Parameters: map[string]string{ - "file_id": "File or shared drive ID", - "permission_id": "Permission ID", - "role": "New role: owner, organizer, fileOrganizer, writer, commenter, reader", - "expiration_time": "RFC3339 expiration timestamp", - "transfer_ownership": "true/false", - "supports_all_drives": "true/false (default true)", - "body": "Raw JSON patch body — overrides convenience args", - }, - Required: []string{"file_id", "permission_id"}, - }, - { - Name: mcp.ToolName("gdrive_delete_permission"), Description: "Revoke a permission (unshare).", - Parameters: map[string]string{ - "file_id": "File or shared drive ID", - "permission_id": "Permission ID", - "supports_all_drives": "true/false (default true)", - }, - Required: []string{"file_id", "permission_id"}, - }, - - // ── Revisions (file version history) ──────────────────────────── - { - Name: mcp.ToolName("gdrive_list_revisions"), Description: "List a file's revision history.", - Parameters: map[string]string{ - "file_id": "File ID", - "page_size": "Max results per page", - "page_token": "Token for next page", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_get_revision"), Description: "Get a specific revision's metadata.", - Parameters: map[string]string{ - "file_id": "File ID", - "revision_id": "Revision ID", - "fields": "Field selector", - }, - Required: []string{"file_id", "revision_id"}, - }, - { - Name: mcp.ToolName("gdrive_update_revision"), Description: "Update a revision (e.g. keepForever to pin it from auto-cleanup).", - Parameters: map[string]string{ - "file_id": "File ID", - "revision_id": "Revision ID", - "keep_forever": "true/false — pin from auto-cleanup", - "published": "true/false — publish revision", - "publish_auto": "true/false — auto-publish subsequent revisions", - "published_outside_domain": "true/false", - "body": "Raw JSON patch body — overrides convenience args", - }, - Required: []string{"file_id", "revision_id"}, - }, - { - Name: mcp.ToolName("gdrive_delete_revision"), Description: "Permanently delete a file revision.", - Parameters: map[string]string{ - "file_id": "File ID", - "revision_id": "Revision ID", - }, - Required: []string{"file_id", "revision_id"}, - }, - - // ── Comments + replies ────────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_list_comments"), Description: "List comments on a file.", - Parameters: map[string]string{ - "file_id": "File ID", - "page_size": "Max results per page", - "page_token": "Token for next page", - "include_deleted": "true/false", - "start_modified_time": "RFC3339 — only comments modified at/after this time", - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("gdrive_get_comment"), Description: "Get a comment by ID, including its replies.", - Parameters: map[string]string{ - "file_id": "File ID", - "comment_id": "Comment ID", - "include_deleted": "true/false", - }, - Required: []string{"file_id", "comment_id"}, - }, - { - Name: mcp.ToolName("gdrive_create_comment"), Description: "Create a new comment on a file. Optionally anchored to a specific quote/range.", - Parameters: map[string]string{ - "file_id": "File ID", - "content": "Comment body (plain text or HTML)", - "anchor": "Anchor JSON string — for anchored comments", - "quoted_file_content": "JSON object string with mimeType+value the comment quotes", - "body": "Raw JSON comment body — overrides convenience args", - }, - Required: []string{"file_id", "content"}, - }, - { - Name: mcp.ToolName("gdrive_update_comment"), Description: "Update a comment's content or resolved status.", - Parameters: map[string]string{ - "file_id": "File ID", - "comment_id": "Comment ID", - "content": "New content", - "resolved": "true/false", - "body": "Raw JSON patch body — overrides convenience args", - }, - Required: []string{"file_id", "comment_id"}, - }, - { - Name: mcp.ToolName("gdrive_delete_comment"), Description: "Delete a comment.", - Parameters: map[string]string{ - "file_id": "File ID", - "comment_id": "Comment ID", - }, - Required: []string{"file_id", "comment_id"}, - }, - { - Name: mcp.ToolName("gdrive_create_reply"), Description: "Reply to a comment.", - Parameters: map[string]string{ - "file_id": "File ID", - "comment_id": "Comment ID", - "content": "Reply content", - "action": "Action: resolve or reopen (optional)", - "body": "Raw JSON reply body — overrides convenience args", - }, - Required: []string{"file_id", "comment_id", "content"}, - }, - - // ── Shared drives ─────────────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_list_drives"), Description: "List the shared drives the user has access to.", - Parameters: map[string]string{ - "page_size": "Max results per page", - "page_token": "Token for next page", - "q": "Search query (e.g. \"name contains 'Engineering'\")", - "use_domain_admin_access": "true/false — list all shared drives in the domain", - }, - }, - { - Name: mcp.ToolName("gdrive_get_drive"), Description: "Get metadata for a single shared drive.", - Parameters: map[string]string{ - "drive_id": "Shared drive ID", - "use_domain_admin_access": "true/false", - }, - Required: []string{"drive_id"}, - }, - { - Name: mcp.ToolName("gdrive_create_drive"), Description: "Create a new shared drive.", - Parameters: map[string]string{ - "name": "Shared drive name", - "request_id": "Unique idempotency key (auto-generated if omitted)", - "theme_id": "Theme ID — for the drive cover image", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gdrive_update_drive"), Description: "Update a shared drive's metadata (name, theme, restrictions).", - Parameters: map[string]string{ - "drive_id": "Shared drive ID", - "name": "New name", - "theme_id": "New theme ID", - "body": "Raw JSON patch body — overrides convenience args (use for restrictions, hidden, etc.)", - "use_domain_admin_access": "true/false", - }, - Required: []string{"drive_id"}, - }, - { - Name: mcp.ToolName("gdrive_delete_drive"), Description: "Delete a shared drive. The drive must be empty.", - Parameters: map[string]string{ - "drive_id": "Shared drive ID", - "allow_item_deletion": "true/false — delete non-empty drive (domain admin only)", - "use_domain_admin_access": "true/false", - }, - Required: []string{"drive_id"}, - }, - { - Name: mcp.ToolName("gdrive_hide_drive"), Description: "Hide a shared drive from the user's default view.", - Parameters: map[string]string{ - "drive_id": "Shared drive ID", - }, - Required: []string{"drive_id"}, - }, - { - Name: mcp.ToolName("gdrive_unhide_drive"), Description: "Restore a shared drive to the user's default view.", - Parameters: map[string]string{ - "drive_id": "Shared drive ID", - }, - Required: []string{"drive_id"}, - }, - - // ── About ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gdrive_get_about"), Description: "Get information about the authenticated user and their Drive (storage quota, supported MIME types, etc.).", - Parameters: map[string]string{ - "fields": "Field selector (default '*'). Example: 'user,storageQuota'", - }, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gdrive/tools.yaml b/integrations/gdrive/tools.yaml new file mode 100644 index 00000000..f2c05f81 --- /dev/null +++ b/integrations/gdrive/tools.yaml @@ -0,0 +1,460 @@ +version: 1 +tools: + gdrive_list_files: + description: "Search and list files and folders in Drive. Start here for finding documents, photos, folders, or anything else by name, owner, or type. Uses Drive's expressive query syntax (e.g. \"name contains 'budget'\", \"mimeType='application/vnd.google-apps.folder'\")." + parameters: + q: + description: "Drive search query (e.g. \"name contains 'report'\", \"'' in parents\", \"mimeType='application/vnd.google-apps.document'\", \"trashed=false\"). See https://developers.google.com/drive/api/guides/search-files" + page_size: + description: "Max results per page (default 100, max 1000)" + page_token: + description: "Token for next page" + order_by: + description: "Comma-separated sort keys with optional 'desc' suffix: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime" + fields: + description: "Selector for response fields (e.g. 'nextPageToken,files(id,name,mimeType,parents)'). Defaults to a useful subset" + corpora: + description: "Bodies of items to query: user, drive, allDrives, domain" + drive_id: + description: "ID of the shared drive to search (required when corpora=drive)" + include_items_from_all_drives: + description: "Include items from shared drives (true/false, requires supports_all_drives=true)" + spaces: + description: "Comma-separated spaces: drive, appDataFolder" + supports_all_drives: + description: "true/false (default true)" + gdrive_get_file: + description: "Get metadata for a single file or folder (name, mimeType, size, parents, modifiedTime, owners, permissions, etc.). Use this for everything except the file content itself — for content use gdrive_download_file or gdrive_export_file." + parameters: + file_id: + description: "File or folder ID" + required: true + fields: + description: "Field selector (default '*' returns all). Example: 'id,name,mimeType,parents,size,modifiedTime'" + supports_all_drives: + description: "true/false (default true)" + gdrive_download_file: + description: "Download the binary content of a non-Google file (PDFs, images, ZIPs, CSV, etc.). For Google Docs/Sheets/Slides use gdrive_export_file. Response is a JSON envelope with base64-encoded content plus content_type, or text content inline when content_type is text/*." + parameters: + file_id: + description: "File ID" + required: true + acknowledge_abuse: + description: "Set to true to download a file flagged as malicious" + supports_all_drives: + description: "true/false (default true)" + max_bytes: + description: "Cap downloaded bytes (default 5_000_000, hard max 10_000_000)" + gdrive_export_file: + description: "Export a Google Docs Editors file (Docs, Sheets, Slides, Drawings) as a different MIME type. Common export types: 'application/pdf', 'text/plain', 'text/csv', 'text/markdown' (for Docs), 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' (.docx). Response is a JSON envelope with content_type plus either base64 content or inline text when content_type is text/*." + parameters: + file_id: + description: "Google Docs/Sheets/Slides/Drawings file ID" + required: true + mime_type: + description: "Target MIME type to export as" + required: true + max_bytes: + description: "Cap exported bytes (default 5_000_000, hard max 10_000_000)" + gdrive_create_file: + description: "Create a new file or upload content. Two modes: (1) metadata-only — pass name/mime_type/parents; (2) with content — pass content (UTF-8 string) or content_base64 (binary). Use gdrive_create_folder for folders specifically." + parameters: + name: + description: "File name" + required: true + mime_type: + description: "MIME type (e.g. 'text/plain', 'application/pdf', 'application/vnd.google-apps.document' to create a new Google Doc from plain text content)" + parents: + description: "Comma-separated parent folder IDs (omit for My Drive root)" + description: + description: "File description" + content: + description: "Text content (mutually exclusive with content_base64)" + content_base64: + description: "Base64-encoded binary content (mutually exclusive with content)" + starred: + description: "true/false" + app_properties: + description: "JSON object string for appProperties" + properties: + description: "JSON object string for properties" + body: + description: "Raw JSON file metadata body — overrides convenience args except content/content_base64" + supports_all_drives: + description: "true/false (default true)" + gdrive_update_file: + description: "Update an existing file's metadata and/or content. To rename, change parents, or update content. Use gdrive_trash_file for soft delete." + parameters: + file_id: + description: "File ID" + required: true + name: + description: "New name" + mime_type: + description: "New MIME type" + description: + description: "New description" + starred: + description: "true/false" + trashed: + description: "true/false (prefer gdrive_trash_file)" + add_parents: + description: "Comma-separated parent IDs to add" + remove_parents: + description: "Comma-separated parent IDs to remove" + content: + description: "Text content to overwrite the file with" + content_base64: + description: "Base64-encoded binary content to overwrite the file with" + body: + description: "Raw JSON file metadata body — overrides convenience args" + supports_all_drives: + description: "true/false (default true)" + gdrive_copy_file: + description: "Copy a file to a new file (metadata + content). Note: cannot copy folders (use gdrive_list_files + repeated copies)." + parameters: + file_id: + description: "Source file ID" + required: true + name: + description: "New file name (defaults to 'Copy of ')" + parents: + description: "Comma-separated parent folder IDs" + description: + description: "New description" + body: + description: "Raw JSON metadata body — overrides convenience args" + supports_all_drives: + description: "true/false (default true)" + gdrive_delete_file: + description: "Permanently delete a file (no trash). Cannot be undone. For soft delete use gdrive_trash_file." + parameters: + file_id: + description: "File ID" + required: true + supports_all_drives: + description: "true/false (default true)" + gdrive_trash_file: + description: "Move a file to the trash (soft delete, reversible via gdrive_untrash_file)." + parameters: + file_id: + description: "File ID" + required: true + supports_all_drives: + description: "true/false (default true)" + gdrive_untrash_file: + description: "Restore a file from the trash." + parameters: + file_id: + description: "File ID" + required: true + supports_all_drives: + description: "true/false (default true)" + gdrive_empty_trash: + description: "Permanently delete all files in the user's trash. Cannot be undone." + parameters: + drive_id: + description: "Shared drive ID (omit to empty My Drive trash)" + gdrive_create_folder: + description: "Create a new folder. Convenience wrapper around gdrive_create_file with mime_type set to application/vnd.google-apps.folder." + parameters: + name: + description: "Folder name" + required: true + parents: + description: "Comma-separated parent folder IDs (omit for My Drive root)" + description: + description: "Folder description" + supports_all_drives: + description: "true/false (default true)" + gdrive_generate_ids: + description: "Generate a set of file IDs that can be used in subsequent insert/copy requests. Useful for pre-allocating IDs before uploading." + parameters: + count: + description: "Number of IDs to generate (default 10, max 1000)" + space: + description: "Space the IDs are for: drive (default) or appDataFolder" + type: + description: "Resource type the IDs are for: files (default) or shortcuts" + gdrive_list_permissions: + description: "List the sharing permissions on a file or shared drive. Shows who has access at what role." + parameters: + file_id: + description: "File or shared drive ID" + required: true + page_size: + description: "Max results per page" + page_token: + description: "Token for next page" + supports_all_drives: + description: "true/false (default true)" + use_domain_admin_access: + description: "true/false — issue as domain administrator" + gdrive_get_permission: + description: "Get a specific permission entry by ID." + parameters: + file_id: + description: "File or shared drive ID" + required: true + permission_id: + description: "Permission ID" + required: true + supports_all_drives: + description: "true/false (default true)" + gdrive_create_permission: + description: "Share a file or folder. Grants a role to a user, group, domain, or anyone. By default sends an email notification." + parameters: + file_id: + description: "File or shared drive ID" + required: true + role: + description: "Role: owner, organizer, fileOrganizer, writer, commenter, reader" + type: + description: "Grantee type: user, group, domain, anyone" + email_address: + description: "Email (required for user/group)" + domain: + description: "Domain (required for type=domain)" + allow_file_discovery: + description: "true/false (for type=domain or anyone)" + send_notification_email: + description: "true/false (default true for user/group)" + email_message: + description: "Custom message to include in the notification email" + transfer_ownership: + description: "true/false — required when changing owner" + move_to_new_owners_root: + description: "true/false — when transferring ownership in My Drive" + supports_all_drives: + description: "true/false (default true)" + body: + description: "Raw JSON permission body — overrides convenience args" + gdrive_update_permission: + description: "Update a permission's role." + parameters: + file_id: + description: "File or shared drive ID" + required: true + permission_id: + description: "Permission ID" + required: true + role: + description: "New role: owner, organizer, fileOrganizer, writer, commenter, reader" + expiration_time: + description: "RFC3339 expiration timestamp" + transfer_ownership: + description: "true/false" + supports_all_drives: + description: "true/false (default true)" + body: + description: "Raw JSON patch body — overrides convenience args" + gdrive_delete_permission: + description: "Revoke a permission (unshare)." + parameters: + file_id: + description: "File or shared drive ID" + required: true + permission_id: + description: "Permission ID" + required: true + supports_all_drives: + description: "true/false (default true)" + gdrive_list_revisions: + description: "List a file's revision history." + parameters: + file_id: + description: "File ID" + required: true + page_size: + description: "Max results per page" + page_token: + description: "Token for next page" + gdrive_get_revision: + description: "Get a specific revision's metadata." + parameters: + file_id: + description: "File ID" + required: true + revision_id: + description: "Revision ID" + required: true + fields: + description: "Field selector" + gdrive_update_revision: + description: "Update a revision (e.g. keepForever to pin it from auto-cleanup)." + parameters: + file_id: + description: "File ID" + required: true + revision_id: + description: "Revision ID" + required: true + keep_forever: + description: "true/false — pin from auto-cleanup" + published: + description: "true/false — publish revision" + publish_auto: + description: "true/false — auto-publish subsequent revisions" + published_outside_domain: + description: "true/false" + body: + description: "Raw JSON patch body — overrides convenience args" + gdrive_delete_revision: + description: "Permanently delete a file revision." + parameters: + file_id: + description: "File ID" + required: true + revision_id: + description: "Revision ID" + required: true + gdrive_list_comments: + description: "List comments on a file." + parameters: + file_id: + description: "File ID" + required: true + page_size: + description: "Max results per page" + page_token: + description: "Token for next page" + include_deleted: + description: "true/false" + start_modified_time: + description: "RFC3339 — only comments modified at/after this time" + gdrive_get_comment: + description: "Get a comment by ID, including its replies." + parameters: + file_id: + description: "File ID" + required: true + comment_id: + description: "Comment ID" + required: true + include_deleted: + description: "true/false" + gdrive_create_comment: + description: "Create a new comment on a file. Optionally anchored to a specific quote/range." + parameters: + file_id: + description: "File ID" + required: true + content: + description: "Comment body (plain text or HTML)" + required: true + anchor: + description: "Anchor JSON string — for anchored comments" + quoted_file_content: + description: "JSON object string with mimeType+value the comment quotes" + body: + description: "Raw JSON comment body — overrides convenience args" + gdrive_update_comment: + description: "Update a comment's content or resolved status." + parameters: + file_id: + description: "File ID" + required: true + comment_id: + description: "Comment ID" + required: true + content: + description: "New content" + resolved: + description: "true/false" + body: + description: "Raw JSON patch body — overrides convenience args" + gdrive_delete_comment: + description: "Delete a comment." + parameters: + file_id: + description: "File ID" + required: true + comment_id: + description: "Comment ID" + required: true + gdrive_create_reply: + description: "Reply to a comment." + parameters: + file_id: + description: "File ID" + required: true + comment_id: + description: "Comment ID" + required: true + content: + description: "Reply content" + required: true + action: + description: "Action: resolve or reopen (optional)" + body: + description: "Raw JSON reply body — overrides convenience args" + gdrive_list_drives: + description: "List the shared drives the user has access to." + parameters: + page_size: + description: "Max results per page" + page_token: + description: "Token for next page" + q: + description: "Search query (e.g. \"name contains 'Engineering'\")" + use_domain_admin_access: + description: "true/false — list all shared drives in the domain" + gdrive_get_drive: + description: "Get metadata for a single shared drive." + parameters: + drive_id: + description: "Shared drive ID" + required: true + use_domain_admin_access: + description: "true/false" + gdrive_create_drive: + description: "Create a new shared drive." + parameters: + name: + description: "Shared drive name" + required: true + request_id: + description: "Unique idempotency key (auto-generated if omitted)" + theme_id: + description: "Theme ID — for the drive cover image" + gdrive_update_drive: + description: "Update a shared drive's metadata (name, theme, restrictions)." + parameters: + drive_id: + description: "Shared drive ID" + required: true + name: + description: "New name" + theme_id: + description: "New theme ID" + body: + description: "Raw JSON patch body — overrides convenience args (use for restrictions, hidden, etc.)" + use_domain_admin_access: + description: "true/false" + gdrive_delete_drive: + description: "Delete a shared drive. The drive must be empty." + parameters: + drive_id: + description: "Shared drive ID" + required: true + allow_item_deletion: + description: "true/false — delete non-empty drive (domain admin only)" + use_domain_admin_access: + description: "true/false" + gdrive_hide_drive: + description: "Hide a shared drive from the user's default view." + parameters: + drive_id: + description: "Shared drive ID" + required: true + gdrive_unhide_drive: + description: "Restore a shared drive to the user's default view." + parameters: + drive_id: + description: "Shared drive ID" + required: true + gdrive_get_about: + description: "Get information about the authenticated user and their Drive (storage quota, supported MIME types, etc.)." + parameters: + fields: + description: "Field selector (default '*'). Example: 'user,storageQuota'" diff --git a/integrations/gforms/tools.go b/integrations/gforms/tools.go index 2f88ca6c..894c656a 100644 --- a/integrations/gforms/tools.go +++ b/integrations/gforms/tools.go @@ -1,55 +1,12 @@ package gforms -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Form lifecycle ────────────────────────────────────────────── - { - Name: mcp.ToolName("gforms_get_form"), Description: "Retrieve (get) a Google Forms form by ID, including all questions (items), info (title/description), settings, and the linked responder URL. Start here for surveys, quizzes, polls, questionnaires, feedback forms — to discover question item IDs for batchUpdate, inspect existing form structure, or read the form's response-collection settings. Use gforms_list_responses to fetch submitted answers.", - Parameters: map[string]string{ - "form_id": "The Google Forms form ID (the long string in the URL between /d/ and /edit)", - }, - Required: []string{"form_id"}, - }, - { - Name: mcp.ToolName("gforms_create_form"), Description: "Create a new Google Forms form (survey, quiz, poll, questionnaire). Only the title can be set at creation time per the Forms API; add questions via gforms_batch_update afterward. Returns the new form including its formId and the linked responderUri (public URL for submitters).", - Parameters: map[string]string{ - "title": "Form title (shown to respondents)", - "document_title": "Optional document title (shown in Drive). Defaults to the form title.", - }, - Required: []string{"title"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Form structure mutation ───────────────────────────────────── - { - Name: mcp.ToolName("gforms_batch_update"), Description: "Apply a batch of edit requests to a Google Forms form — the primary mutation API. Use for adding/deleting questions (items), updating titles or descriptions, changing settings (quiz mode, response collection), and moving items. Pass 'requests' as a JSON array of Forms API Request objects (e.g. [{\"createItem\":{\"item\":{\"title\":\"Q1\",\"questionItem\":{\"question\":{\"textQuestion\":{}}}},\"location\":{\"index\":0}}}]). For pure read access, use gforms_get_form instead.", - Parameters: map[string]string{ - "form_id": "The form ID", - "requests": "JSON array of Forms API Request objects", - "include_form_in_response": "Optional boolean — if true, the response includes the updated form", - "write_control_revision": "Optional required revision ID for optimistic concurrency (will fail if the form has been edited since this revision)", - "write_control_target_revision": "Optional target revision ID — overrides required_revision_id when present", - }, - Required: []string{"form_id", "requests"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Response (submission) access ──────────────────────────────── - { - Name: mcp.ToolName("gforms_list_responses"), Description: "List submitted responses (answers, submissions) for a Google Forms form. Returns paginated FormResponse objects with respondent answers keyed by question item ID. Use to analyze survey results, quiz submissions, feedback, or any collected form data. Combine with gforms_get_form to map question IDs to question text.", - Parameters: map[string]string{ - "form_id": "The form ID", - "page_size": "Optional max responses per page (1-5000, default 5000)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "filter": "Optional filter (e.g. 'timestamp > 2024-01-01T00:00:00Z') — see Forms API filter syntax", - }, - Required: []string{"form_id"}, - }, - { - Name: mcp.ToolName("gforms_get_response"), Description: "Retrieve a single submitted response (answer, submission) from a Google Forms form by its response ID. Lighter than gforms_list_responses when you already have the responseId.", - Parameters: map[string]string{ - "form_id": "The form ID", - "response_id": "The response ID (from FormResponse.responseId)", - }, - Required: []string{"form_id", "response_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gforms/tools.yaml b/integrations/gforms/tools.yaml new file mode 100644 index 00000000..54e055f3 --- /dev/null +++ b/integrations/gforms/tools.yaml @@ -0,0 +1,52 @@ +version: 1 +tools: + gforms_get_form: + description: "Retrieve (get) a Google Forms form by ID, including all questions (items), info (title/description), settings, and the linked responder URL. Start here for surveys, quizzes, polls, questionnaires, feedback forms — to discover question item IDs for batchUpdate, inspect existing form structure, or read the form's response-collection settings. Use gforms_list_responses to fetch submitted answers." + parameters: + form_id: + description: "The Google Forms form ID (the long string in the URL between /d/ and /edit)" + required: true + gforms_create_form: + description: "Create a new Google Forms form (survey, quiz, poll, questionnaire). Only the title can be set at creation time per the Forms API; add questions via gforms_batch_update afterward. Returns the new form including its formId and the linked responderUri (public URL for submitters)." + parameters: + title: + description: "Form title (shown to respondents)" + required: true + document_title: + description: "Optional document title (shown in Drive). Defaults to the form title." + gforms_batch_update: + description: "Apply a batch of edit requests to a Google Forms form — the primary mutation API. Use for adding/deleting questions (items), updating titles or descriptions, changing settings (quiz mode, response collection), and moving items. Pass 'requests' as a JSON array of Forms API Request objects (e.g. [{\"createItem\":{\"item\":{\"title\":\"Q1\",\"questionItem\":{\"question\":{\"textQuestion\":{}}}},\"location\":{\"index\":0}}}]). For pure read access, use gforms_get_form instead." + parameters: + form_id: + description: "The form ID" + required: true + requests: + description: "JSON array of Forms API Request objects" + required: true + include_form_in_response: + description: "Optional boolean — if true, the response includes the updated form" + write_control_revision: + description: "Optional required revision ID for optimistic concurrency (will fail if the form has been edited since this revision)" + write_control_target_revision: + description: "Optional target revision ID — overrides required_revision_id when present" + gforms_list_responses: + description: "List submitted responses (answers, submissions) for a Google Forms form. Returns paginated FormResponse objects with respondent answers keyed by question item ID. Use to analyze survey results, quiz submissions, feedback, or any collected form data. Combine with gforms_get_form to map question IDs to question text." + parameters: + form_id: + description: "The form ID" + required: true + page_size: + description: "Optional max responses per page (1-5000, default 5000)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + filter: + description: "Optional filter (e.g. 'timestamp > 2024-01-01T00:00:00Z') — see Forms API filter syntax" + gforms_get_response: + description: "Retrieve a single submitted response (answer, submission) from a Google Forms form by its response ID. Lighter than gforms_list_responses when you already have the responseId." + parameters: + form_id: + description: "The form ID" + required: true + response_id: + description: "The response ID (from FormResponse.responseId)" + required: true diff --git a/integrations/github/tools.go b/integrations/github/tools.go index 48104217..f79db33b 100644 --- a/integrations/github/tools.go +++ b/integrations/github/tools.go @@ -1,1046 +1,12 @@ package github -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Repositories ────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_search_repos"), Description: "Search GitHub repositories. Start here to find repos by name, topic, or language.", - Parameters: map[string]string{"query": "Search query", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("github_get_repo"), Description: "Get a repository by owner/name. Use after search_repos or when you already know the owner/repo.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_user_repos"), Description: "List repositories for a user. Use when you know the username; prefer search_repos for keyword discovery.", - Parameters: map[string]string{"username": "GitHub username", "type": "Type: all, owner, member (default: owner)", "sort": "Sort: created, updated, pushed, full_name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"username"}, - }, - { - Name: mcp.ToolName("github_list_org_repos"), Description: "List repositories for an organization. Use when you know the org; prefer search_repos for keyword discovery.", - Parameters: map[string]string{"org": "Organization name", "type": "Type: all, public, private, forks, sources, member", "sort": "Sort: created, updated, pushed, full_name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_create_repo"), Description: "Create a repository for the authenticated user or an org", - Parameters: map[string]string{"name": "Repository name", "org": "Organization (omit for user repo)", "description": "Description", "private": "Private repo (true/false)", "auto_init": "Initialize with README (true/false)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("github_delete_repo"), Description: "Delete a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_branches"), Description: "List branches of a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_branch"), Description: "Get a specific branch", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Branch name"}, - Required: []string{"owner", "repo", "branch"}, - }, - { - Name: mcp.ToolName("github_list_tags"), Description: "List tags of a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_contributors"), Description: "List contributors to a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_languages"), Description: "List languages used in a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_topics"), Description: "List repository topics", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_readme"), Description: "Get the README for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref (branch/tag/sha)"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_file_contents"), Description: "Get file or directory contents from a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "path": "File path", "ref": "Git ref (branch/tag/sha)"}, - Required: []string{"owner", "repo", "path"}, - }, - { - Name: mcp.ToolName("github_create_update_file"), Description: "Create or update a file in a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "path": "File path", "message": "Commit message", "content": "Raw file content (plain text, not base64 — encoding is handled automatically)", "sha": "SHA of file being replaced (required for update)", "branch": "Target branch"}, - Required: []string{"owner", "repo", "path", "message", "content"}, - }, - { - Name: mcp.ToolName("github_delete_file"), Description: "Delete a file from a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "path": "File path", "message": "Commit message", "sha": "SHA of file to delete", "branch": "Target branch"}, - Required: []string{"owner", "repo", "path", "message", "sha"}, - }, - { - Name: mcp.ToolName("github_list_forks"), Description: "List forks of a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sort": "Sort: newest, oldest, stargazers, watchers", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_fork"), Description: "Fork a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "organization": "Organization to fork into"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_collaborators"), Description: "List collaborators on a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_commit_activity"), Description: "Get the last year of commit activity (weekly)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_repo_teams"), Description: "List teams with access to a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_compare_commits"), Description: "Compare two commits, branches, or tags. Use to see what changed between refs (commit list and diff). Start here for 'what changed in prod' queries.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "base": "Base ref (branch, tag, or SHA)", "head": "Head ref (branch, tag, or SHA)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "base", "head"}, - }, - { - Name: mcp.ToolName("github_merge_upstream"), Description: "Sync a fork branch with the upstream repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Branch to sync"}, - Required: []string{"owner", "repo", "branch"}, - }, - { - Name: mcp.ToolName("github_list_autolinks"), Description: "List autolink references for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number"}, - Required: []string{"owner", "repo"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Repositories (extended) ─────────────────────────────────────── - { - Name: mcp.ToolName("github_edit_repo"), Description: "Update a repository's settings (description, visibility, default branch, etc.)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "description": "New description", "homepage": "Homepage URL", "default_branch": "Default branch name", "private": "Private (true/false)", "archived": "Archived (true/false)", "has_issues": "Enable issues (true/false)", "has_projects": "Enable projects (true/false)", "has_wiki": "Enable wiki (true/false)"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_replace_topics"), Description: "Replace all topics on a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "topics": "Comma-separated topic names"}, - Required: []string{"owner", "repo", "topics"}, - }, - { - Name: mcp.ToolName("github_rename_branch"), Description: "Rename a branch", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Current branch name", "new_name": "New branch name"}, - Required: []string{"owner", "repo", "branch", "new_name"}, - }, - { - Name: mcp.ToolName("github_add_collaborator"), Description: "Add a collaborator to a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "username": "User to add", "permission": "Permission: pull, triage, push, maintain, admin"}, - Required: []string{"owner", "repo", "username"}, - }, - { - Name: mcp.ToolName("github_remove_collaborator"), Description: "Remove a collaborator from a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "username": "User to remove"}, - Required: []string{"owner", "repo", "username"}, - }, - { - Name: mcp.ToolName("github_get_combined_status"), Description: "Get the combined commit status for a ref (aggregates all status checks)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref (SHA, branch, or tag)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_list_statuses"), Description: "List commit statuses for a ref", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref (SHA, branch, or tag)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_create_status"), Description: "Create a commit status", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Commit SHA", "state": "State: error, failure, pending, success", "target_url": "URL to associate with status", "description": "Short description", "context": "Status context identifier"}, - Required: []string{"owner", "repo", "sha", "state"}, - }, - { - Name: mcp.ToolName("github_list_deployments"), Description: "List deployments for a repository. Start here for deploy status, recent deploys, and rollout history.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "environment": "Filter by environment", "ref": "Filter by ref", "task": "Filter by task", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_deployment"), Description: "Get a single deployment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "deployment_id": "Deployment ID"}, - Required: []string{"owner", "repo", "deployment_id"}, - }, - { - Name: mcp.ToolName("github_create_deployment"), Description: "Create a deployment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Ref to deploy (branch, tag, or SHA)", "task": "Task (default: deploy)", "environment": "Environment name", "description": "Description", "auto_merge": "Auto-merge default branch into ref (true/false)"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_list_deployment_statuses"), Description: "List statuses for a deployment. Use after list_deployments to check deploy progress or failure.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "deployment_id": "Deployment ID", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "deployment_id"}, - }, - { - Name: mcp.ToolName("github_create_deployment_status"), Description: "Create a deployment status", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "deployment_id": "Deployment ID", "state": "State: error, failure, inactive, in_progress, queued, pending, success", "description": "Description", "log_url": "Log URL", "environment": "Override environment name"}, - Required: []string{"owner", "repo", "deployment_id", "state"}, - }, - { - Name: mcp.ToolName("github_list_environments"), Description: "List environments for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_environment"), Description: "Get a single environment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "environment": "Environment name"}, - Required: []string{"owner", "repo", "environment"}, - }, - { - Name: mcp.ToolName("github_get_branch_protection"), Description: "Get branch protection rules", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Branch name"}, - Required: []string{"owner", "repo", "branch"}, - }, - { - Name: mcp.ToolName("github_remove_branch_protection"), Description: "Remove branch protection rules", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Branch name"}, - Required: []string{"owner", "repo", "branch"}, - }, - { - Name: mcp.ToolName("github_list_rulesets"), Description: "List repository rulesets", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_ruleset"), Description: "Get a repository ruleset by ID", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ruleset_id": "Ruleset ID"}, - Required: []string{"owner", "repo", "ruleset_id"}, - }, - { - Name: mcp.ToolName("github_get_rules_for_branch"), Description: "Get active rules that apply to a branch", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "branch": "Branch name"}, - Required: []string{"owner", "repo", "branch"}, - }, - { - Name: mcp.ToolName("github_list_traffic_views"), Description: "Get repository traffic page views (last 14 days, push access required)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "per": "Aggregation period: day, week"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_traffic_clones"), Description: "Get repository traffic clones (last 14 days, push access required)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "per": "Aggregation period: day, week"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_traffic_referrers"), Description: "Get top referral sources for a repository (push access required)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_traffic_paths"), Description: "Get popular content paths for a repository (push access required)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_community_health"), Description: "Get community health metrics for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_dispatch_event"), Description: "Trigger a repository dispatch event", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "event_type": "Custom event type string"}, - Required: []string{"owner", "repo", "event_type"}, - }, - { - Name: mcp.ToolName("github_merge_branch"), Description: "Merge a branch into another (not a PR merge — use github_merge_pull for PRs)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "base": "Branch to merge into", "head": "Branch to merge from", "commit_message": "Merge commit message"}, - Required: []string{"owner", "repo", "base", "head"}, - }, - { - Name: mcp.ToolName("github_edit_release"), Description: "Update a release", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "release_id": "Release ID", "tag_name": "New tag name", "name": "New release name", "body": "New release notes", "draft": "Draft (true/false)", "prerelease": "Pre-release (true/false)"}, - Required: []string{"owner", "repo", "release_id"}, - }, - { - Name: mcp.ToolName("github_generate_release_notes"), Description: "Auto-generate release notes content between two tags", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "tag_name": "Tag for the release", "previous_tag_name": "Previous tag to compare against", "target_commitish": "Branch or SHA to tag"}, - Required: []string{"owner", "repo", "tag_name"}, - }, - { - Name: mcp.ToolName("github_list_commit_comments"), Description: "List comments on a specific commit", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Commit SHA", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "sha"}, - }, - { - Name: mcp.ToolName("github_create_commit_comment"), Description: "Create a comment on a commit", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Commit SHA", "body": "Comment body", "path": "Relative file path", "position": "Line position in the diff"}, - Required: []string{"owner", "repo", "sha", "body"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Issues ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_issues"), Description: "List issues for a repository. Start here for issue workflows when you know the repo. For cross-repo search, use search_issues.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: open, closed, all", "labels": "Comma-separated label names", "sort": "Sort: created, updated, comments", "direction": "Direction: asc, desc", "assignee": "Filter by assignee username", "milestone": "Milestone number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_issue"), Description: "Get a single issue with full details. Use after list_issues or search_issues to drill into a specific issue.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_create_issue"), Description: "Create an issue. Requires owner, repo, and title at minimum.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "title": "Issue title", "body": "Issue body (markdown)", "assignees": "Comma-separated assignee usernames", "labels": "Comma-separated label names", "milestone": "Milestone number"}, - Required: []string{"owner", "repo", "title"}, - }, - { - Name: mcp.ToolName("github_update_issue"), Description: "Update an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "title": "New title", "body": "New body", "state": "State: open, closed", "assignees": "Comma-separated assignee usernames", "labels": "Comma-separated label names", "milestone": "Milestone number"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_list_issue_comments"), Description: "List comments on an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_create_issue_comment"), Description: "Create a comment on an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "body": "Comment body (markdown)"}, - Required: []string{"owner", "repo", "number", "body"}, - }, - { - Name: mcp.ToolName("github_list_issue_labels"), Description: "List labels on an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_add_issue_labels"), Description: "Add labels to an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "labels": "Comma-separated label names to add"}, - Required: []string{"owner", "repo", "number", "labels"}, - }, - { - Name: mcp.ToolName("github_remove_issue_label"), Description: "Remove a label from an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "label": "Label name to remove"}, - Required: []string{"owner", "repo", "number", "label"}, - }, - { - Name: mcp.ToolName("github_lock_issue"), Description: "Lock an issue conversation", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "lock_reason": "Reason: off-topic, too heated, resolved, spam"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_unlock_issue"), Description: "Unlock an issue conversation", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_list_milestones"), Description: "List milestones for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: open, closed, all", "sort": "Sort: due_on, completeness", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_milestone"), Description: "Create a milestone", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "title": "Milestone title", "description": "Description", "due_on": "Due date (ISO 8601 YYYY-MM-DDT00:00:00Z)", "state": "State: open, closed"}, - Required: []string{"owner", "repo", "title"}, - }, - { - Name: mcp.ToolName("github_list_issue_events"), Description: "List events on an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_list_issue_timeline"), Description: "List timeline events for an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_list_assignees"), Description: "List available assignees for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── Issues (extended) ───────────────────────────────────────────── - { - Name: mcp.ToolName("github_update_issue_comment"), Description: "Edit an issue or PR comment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID", "body": "New comment body (markdown)"}, - Required: []string{"owner", "repo", "comment_id", "body"}, - }, - { - Name: mcp.ToolName("github_delete_issue_comment"), Description: "Delete an issue or PR comment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID"}, - Required: []string{"owner", "repo", "comment_id"}, - }, - { - Name: mcp.ToolName("github_update_milestone"), Description: "Update a milestone", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Milestone number", "title": "New title", "description": "New description", "state": "State: open, closed", "due_on": "Due date (ISO 8601)"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_delete_milestone"), Description: "Delete a milestone", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Milestone number"}, - Required: []string{"owner", "repo", "number"}, - }, - { - Name: mcp.ToolName("github_list_labels"), Description: "List all labels in a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_label"), Description: "Create a label in a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Label name", "color": "Color hex code (without #)", "description": "Label description"}, - Required: []string{"owner", "repo", "name", "color"}, - }, - { - Name: mcp.ToolName("github_edit_label"), Description: "Update a label", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Current label name", "new_name": "New label name", "color": "New color hex code (without #)", "description": "New description"}, - Required: []string{"owner", "repo", "name"}, - }, - { - Name: mcp.ToolName("github_delete_label"), Description: "Delete a label from a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Label name"}, - Required: []string{"owner", "repo", "name"}, - }, - { - Name: mcp.ToolName("github_create_issue_reaction"), Description: "Add a reaction to an issue (+1, -1, laugh, confused, heart, hooray, rocket, eyes)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "content": "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes"}, - Required: []string{"owner", "repo", "number", "content"}, - }, - { - Name: mcp.ToolName("github_create_issue_comment_reaction"), Description: "Add a reaction to an issue comment", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID", "content": "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes"}, - Required: []string{"owner", "repo", "comment_id", "content"}, - }, - { - Name: mcp.ToolName("github_list_issue_reactions"), Description: "List reactions on an issue", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "number": "Issue number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "number"}, - }, - - // ── Pull Requests ───────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_pulls"), Description: "List pull requests for a repository. Start here for PR workflows when you know the repo. For cross-repo search, use search_issues with type:pr.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: open, closed, all", "head": "Filter by head user:branch", "base": "Filter by base branch", "sort": "Sort: created, updated, popularity, long-running", "direction": "Direction: asc, desc", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_pull"), Description: "Get a single pull request with full details. Use after list_pulls to drill into a specific PR. For the diff, follow up with get_pull_diff.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_get_pull_diff"), Description: "Get the raw unified diff of a pull request. Use after get_pull for the full code diff.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_create_pull"), Description: "Create a pull request. Requires head branch and base branch.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "title": "PR title", "head": "Head branch (or user:branch for cross-repo)", "base": "Base branch", "body": "PR body (markdown)", "draft": "Create as draft (true/false)"}, - Required: []string{"owner", "repo", "title", "head", "base"}, - }, - { - Name: mcp.ToolName("github_update_pull"), Description: "Update a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "title": "New title", "body": "New body", "state": "State: open, closed", "base": "New base branch"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_list_pull_commits"), Description: "List commits on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_list_pull_files"), Description: "List files changed in a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_list_pull_reviews"), Description: "List reviews on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_create_pull_review"), Description: "Create a review on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "body": "Review body", "event": "Review action: APPROVE, REQUEST_CHANGES, COMMENT"}, - Required: []string{"owner", "repo", "pull_number", "event"}, - }, - { - Name: mcp.ToolName("github_list_pull_comments"), Description: "List review comments on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_create_pull_comment"), Description: "Create a review comment on a pull request diff", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "body": "Comment body", "commit_id": "SHA of the commit to comment on", "path": "Relative file path", "line": "Line number in the diff"}, - Required: []string{"owner", "repo", "pull_number", "body", "commit_id", "path"}, - }, - { - Name: mcp.ToolName("github_get_pull_comment"), Description: "Get a single review comment on a pull request by its comment ID", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID"}, - Required: []string{"owner", "repo", "comment_id"}, - }, - { - Name: mcp.ToolName("github_reply_to_pull_comment"), Description: "Reply to an existing review comment thread on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "body": "Reply body", "comment_id": "ID of the comment to reply to"}, - Required: []string{"owner", "repo", "pull_number", "body", "comment_id"}, - }, - { - Name: mcp.ToolName("github_update_pull_comment"), Description: "Update the body of a review comment on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID", "body": "New comment body (markdown)"}, - Required: []string{"owner", "repo", "comment_id", "body"}, - }, - { - Name: mcp.ToolName("github_delete_pull_comment"), Description: "Delete a review comment on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "comment_id": "Comment ID"}, - Required: []string{"owner", "repo", "comment_id"}, - }, - { - Name: mcp.ToolName("github_merge_pull"), Description: "Merge a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "commit_message": "Merge commit message", "merge_method": "Method: merge, squash, rebase"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_list_requested_reviewers"), Description: "List requested reviewers on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_request_reviewers"), Description: "Request reviewers on a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "reviewers": "Comma-separated usernames", "team_reviewers": "Comma-separated team slugs"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - - // ── Pull Requests (extended) ────────────────────────────────────── - { - Name: mcp.ToolName("github_dismiss_pull_review"), Description: "Dismiss a pull request review", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "review_id": "Review ID", "message": "Dismissal message"}, - Required: []string{"owner", "repo", "pull_number", "review_id", "message"}, - }, - { - Name: mcp.ToolName("github_update_pull_branch"), Description: "Update a PR branch with the latest changes from the base branch", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "expected_head_sha": "Expected SHA of the PR head (for optimistic locking)"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_remove_reviewers"), Description: "Remove requested reviewers from a pull request", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull request number", "reviewers": "Comma-separated usernames", "team_reviewers": "Comma-separated team slugs"}, - Required: []string{"owner", "repo", "pull_number"}, - }, - { - Name: mcp.ToolName("github_list_pulls_with_commit"), Description: "List pull requests that contain a specific commit", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Commit SHA", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "sha"}, - }, - - // ── Git (low-level) ─────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_commit"), Description: "Get a commit by SHA including files changed. For comparing two refs, use compare_commits instead.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Commit SHA"}, - Required: []string{"owner", "repo", "sha"}, - }, - { - Name: mcp.ToolName("github_list_commits"), Description: "List commits on a branch", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Branch name or SHA to start listing from", "path": "Only commits containing this file path", "author": "GitHub login or email to filter by", "since": "Only commits after this date (ISO 8601)", "until": "Only commits before this date (ISO 8601)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_ref"), Description: "Get a git reference (branch or tag)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Reference (e.g., heads/main, tags/v1.0)"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_create_ref"), Description: "Create a git reference (branch or tag)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Full reference path (e.g., refs/heads/new-branch)", "sha": "SHA to point the ref at"}, - Required: []string{"owner", "repo", "ref", "sha"}, - }, - { - Name: mcp.ToolName("github_delete_ref"), Description: "Delete a git reference", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Reference to delete (e.g., heads/old-branch)"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_get_tree"), Description: "Get a git tree (directory listing)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Tree SHA or branch name", "recursive": "Recurse into subtrees (true/false)"}, - Required: []string{"owner", "repo", "sha"}, - }, - { - Name: mcp.ToolName("github_create_tag"), Description: "Create an annotated tag object", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "tag": "Tag name", "message": "Tag message", "sha": "SHA of object to tag", "type": "Object type: commit, tree, blob"}, - Required: []string{"owner", "repo", "tag", "message", "sha"}, - }, - - // ── Users ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_authenticated_user"), Description: "Get the currently authenticated user", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("github_get_user"), Description: "Get a user by username", - Parameters: map[string]string{"username": "GitHub username"}, - Required: []string{"username"}, - }, - { - Name: mcp.ToolName("github_list_user_followers"), Description: "List followers of a user", - Parameters: map[string]string{"username": "GitHub username", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"username"}, - }, - { - Name: mcp.ToolName("github_list_user_following"), Description: "List users that a user follows", - Parameters: map[string]string{"username": "GitHub username", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"username"}, - }, - { - Name: mcp.ToolName("github_list_user_keys"), Description: "List public SSH keys for a user", - Parameters: map[string]string{"username": "GitHub username", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"username"}, - }, - - // ── Organizations ───────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_org"), Description: "Get an organization", - Parameters: map[string]string{"org": "Organization login name"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_list_user_orgs"), Description: "List organizations for a user", - Parameters: map[string]string{"username": "GitHub username (empty for authenticated user)", "page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("github_list_org_members"), Description: "List organization members", - Parameters: map[string]string{"org": "Organization name", "role": "Filter: all, admin, member", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_list_org_teams"), Description: "List teams in an organization", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_get_team_by_slug"), Description: "Get a team by slug", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug"}, - Required: []string{"org", "slug"}, - }, - { - Name: mcp.ToolName("github_list_team_members"), Description: "List members of a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "role": "Filter: all, member, maintainer", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org", "slug"}, - }, - { - Name: mcp.ToolName("github_list_team_repos"), Description: "List repositories for a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org", "slug"}, - }, - - // ── Teams (extended) ────────────────────────────────────────────── - { - Name: mcp.ToolName("github_create_team"), Description: "Create a team in an organization", - Parameters: map[string]string{"org": "Organization name", "name": "Team name", "description": "Team description", "privacy": "Privacy: secret, closed", "permission": "Default permission: pull, push"}, - Required: []string{"org", "name"}, - }, - { - Name: mcp.ToolName("github_edit_team"), Description: "Update a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "name": "New team name", "description": "New description", "privacy": "Privacy: secret, closed", "permission": "Default permission: pull, push"}, - Required: []string{"org", "slug", "name"}, - }, - { - Name: mcp.ToolName("github_delete_team"), Description: "Delete a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug"}, - Required: []string{"org", "slug"}, - }, - { - Name: mcp.ToolName("github_add_team_member"), Description: "Add or update a user's membership in a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "username": "GitHub username", "role": "Role: member, maintainer"}, - Required: []string{"org", "slug", "username"}, - }, - { - Name: mcp.ToolName("github_remove_team_member"), Description: "Remove a user from a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "username": "GitHub username"}, - Required: []string{"org", "slug", "username"}, - }, - { - Name: mcp.ToolName("github_add_team_repo"), Description: "Add a repository to a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "owner": "Repository owner", "repo": "Repository name", "permission": "Permission: pull, triage, push, maintain, admin"}, - Required: []string{"org", "slug", "owner", "repo"}, - }, - { - Name: mcp.ToolName("github_remove_team_repo"), Description: "Remove a repository from a team", - Parameters: map[string]string{"org": "Organization name", "slug": "Team slug", "owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"org", "slug", "owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_pending_org_invitations"), Description: "List pending organization invitations", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_list_outside_collaborators"), Description: "List outside collaborators for an organization", - Parameters: map[string]string{"org": "Organization name", "filter": "Filter: 2fa_disabled, all", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - - // ── Actions (CI/CD) ─────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_workflows"), Description: "List workflows in a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_workflow_runs"), Description: "List workflow runs for a repository. Use to check CI status or recent builds.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "workflow_id": "Workflow ID or filename (e.g., ci.yml)", "branch": "Filter by branch", "event": "Filter by event (push, pull_request, etc.)", "status": "Filter: completed, action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, in_progress, queued, requested, waiting, pending", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_workflow_run"), Description: "Get a specific workflow run. Use after list_workflow_runs for full run details.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_list_workflow_jobs"), Description: "List jobs for a workflow run", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID", "filter": "Filter: latest, all", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_download_workflow_logs"), Description: "Get a URL to download workflow run logs", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_rerun_workflow"), Description: "Re-run a workflow run", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_cancel_workflow_run"), Description: "Cancel a workflow run", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_list_repo_secrets"), Description: "List repository Actions secrets (names only, not values)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_artifacts"), Description: "List artifacts for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_environment_secrets"), Description: "List secrets for an environment (names only)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "environment": "Environment name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "environment"}, - }, - { - Name: mcp.ToolName("github_list_org_secrets"), Description: "List organization Actions secrets (names only)", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - - // ── Actions (extended) ──────────────────────────────────────────── - { - Name: mcp.ToolName("github_trigger_workflow"), Description: "Trigger a workflow dispatch event", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "workflow_id": "Workflow filename (e.g., ci.yml)", "ref": "Git ref to run workflow on (branch or tag)"}, - Required: []string{"owner", "repo", "workflow_id", "ref"}, - }, - { - Name: mcp.ToolName("github_rerun_failed_jobs"), Description: "Re-run only the failed jobs of a workflow run", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_get_workflow_job"), Description: "Get a single workflow job by ID", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "job_id": "Job ID"}, - Required: []string{"owner", "repo", "job_id"}, - }, - { - Name: mcp.ToolName("github_get_workflow_job_logs"), Description: "Get a URL to download a single job's logs", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "job_id": "Job ID"}, - Required: []string{"owner", "repo", "job_id"}, - }, - { - Name: mcp.ToolName("github_delete_workflow_run"), Description: "Delete a workflow run", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "run_id": "Workflow run ID"}, - Required: []string{"owner", "repo", "run_id"}, - }, - { - Name: mcp.ToolName("github_list_repo_variables"), Description: "List repository Actions variables (names and values)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_repo_variable"), Description: "Create a repository Actions variable", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Variable name", "value": "Variable value"}, - Required: []string{"owner", "repo", "name", "value"}, - }, - { - Name: mcp.ToolName("github_update_repo_variable"), Description: "Update a repository Actions variable", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Variable name", "value": "New variable value"}, - Required: []string{"owner", "repo", "name", "value"}, - }, - { - Name: mcp.ToolName("github_delete_repo_variable"), Description: "Delete a repository Actions variable", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "name": "Variable name"}, - Required: []string{"owner", "repo", "name"}, - }, - { - Name: mcp.ToolName("github_list_org_variables"), Description: "List organization Actions variables (names and values)", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_list_env_variables"), Description: "List environment Actions variables (names and values)", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "environment": "Environment name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "environment"}, - }, - { - Name: mcp.ToolName("github_list_runners"), Description: "List self-hosted runners for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_runner_registration_token"), Description: "Create a repository self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token command; tokens are valid for about 1 hour.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_org_runners"), Description: "List self-hosted runners for an organization", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - { - Name: mcp.ToolName("github_create_org_runner_registration_token"), Description: "Create an organization self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token command; tokens are valid for about 1 hour.", - Parameters: map[string]string{"org": "Organization name"}, - Required: []string{"org"}, - }, - - // ── Checks ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_check_runs"), Description: "List check runs for a git reference", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref (SHA, branch, or tag)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "ref"}, - }, - { - Name: mcp.ToolName("github_get_check_run"), Description: "Get a check run by ID", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "check_run_id": "Check run ID"}, - Required: []string{"owner", "repo", "check_run_id"}, - }, - { - Name: mcp.ToolName("github_list_check_suites"), Description: "List check suites for a git reference", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref (SHA, branch, or tag)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "ref"}, - }, - - // ── Releases ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_releases"), Description: "List releases for a repository. Start here for release history, versioning, and what shipped.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_release"), Description: "Get a release by ID", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "release_id": "Release ID"}, - Required: []string{"owner", "repo", "release_id"}, - }, - { - Name: mcp.ToolName("github_get_latest_release"), Description: "Get the latest release for a repository. Use to find what version is current or what shipped most recently.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_release"), Description: "Create a release", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "tag_name": "Tag name for the release", "name": "Release name", "body": "Release notes (markdown)", "draft": "Create as draft (true/false)", "prerelease": "Mark as pre-release (true/false)", "target_commitish": "Branch or SHA to tag (defaults to default branch)"}, - Required: []string{"owner", "repo", "tag_name"}, - }, - { - Name: mcp.ToolName("github_delete_release"), Description: "Delete a release", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "release_id": "Release ID"}, - Required: []string{"owner", "repo", "release_id"}, - }, - { - Name: mcp.ToolName("github_list_release_assets"), Description: "List assets for a release", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "release_id": "Release ID", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo", "release_id"}, - }, - - // ── Gists ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_gists"), Description: "List gists for the authenticated user or a specific user", - Parameters: map[string]string{"username": "GitHub username (empty for authenticated user)", "page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("github_get_gist"), Description: "Get a gist by ID", - Parameters: map[string]string{"id": "Gist ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("github_create_gist"), Description: "Create a gist", - Parameters: map[string]string{"description": "Gist description", "public": "Public gist (true/false)", "filename": "Filename for the gist content", "content": "File content"}, - Required: []string{"filename", "content"}, - }, - - // ── Search ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_search_topics"), Description: "Search GitHub topics", - Parameters: map[string]string{"query": "Search query", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("github_search_labels"), Description: "Search labels in a repository", - Parameters: map[string]string{"repository_id": "Repository numeric ID", "query": "Search query", "sort": "Sort: created, updated", "order": "Order: asc, desc", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"repository_id", "query"}, - }, - { - Name: mcp.ToolName("github_search_code"), Description: "Search code across GitHub repositories. Start here to find files, functions, or strings across repos.", - Parameters: map[string]string{"query": "Search query (supports qualifiers like language:go, repo:owner/name)", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("github_search_issues"), Description: "Search issues and pull requests across GitHub. Start here for cross-repo issue/PR discovery. Add is:pr or is:issue to filter. Use for cross-repo bug, ticket, or PR discovery.", - Parameters: map[string]string{"query": "Search query (supports qualifiers like is:issue, is:pr, repo:owner/name, state:open)", "sort": "Sort: comments, reactions, created, updated", "order": "Order: asc, desc", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("github_search_users"), Description: "Search GitHub users", - Parameters: map[string]string{"query": "Search query (supports qualifiers like location:, language:, followers:>N)", "sort": "Sort: followers, repositories, joined", "order": "Order: asc, desc", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("github_search_commits"), Description: "Search commits across GitHub", - Parameters: map[string]string{"query": "Search query (supports qualifiers like author:, repo:, committer:)", "sort": "Sort: author-date, committer-date", "order": "Order: asc, desc", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"query"}, - }, - - // ── Activity ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_stargazers"), Description: "List stargazers for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_watchers"), Description: "List watchers (subscribers) of a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_notifications"), Description: "List notifications for the authenticated user", - Parameters: map[string]string{"all": "Show all notifications including read (true/false)", "participating": "Only show participating notifications (true/false)", "page": "Page number", "per_page": "Results per page"}, - }, - { - Name: mcp.ToolName("github_list_repo_events"), Description: "List events for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── Activity (extended) ─────────────────────────────────────────── - { - Name: mcp.ToolName("github_mark_notifications_read"), Description: "Mark all notifications as read", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("github_star_repo"), Description: "Star a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_unstar_repo"), Description: "Unstar a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_list_starred"), Description: "List repositories starred by a user", - Parameters: map[string]string{"username": "GitHub username (empty for authenticated user)", "sort": "Sort: created, updated", "direction": "Direction: asc, desc", "page": "Page number", "per_page": "Results per page"}, - }, - - // ── Code Scanning ───────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_code_scanning_alerts"), Description: "List code scanning (SAST) alerts for a repository. Start here for security vulnerabilities found by CodeQL or other analyzers.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: open, closed, dismissed, fixed", "ref": "Git ref to filter by", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_get_code_scanning_alert"), Description: "Get a code scanning alert", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "alert_number": "Alert number"}, - Required: []string{"owner", "repo", "alert_number"}, - }, - - // ── Secret Scanning ─────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_secret_scanning_alerts"), Description: "List secret scanning alerts for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: open, resolved", "secret_type": "Filter by secret type", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── Secret Scanning (extended) ──────────────────────────────────── - { - Name: mcp.ToolName("github_get_secret_scanning_alert"), Description: "Get a single secret scanning alert", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "alert_number": "Alert number"}, - Required: []string{"owner", "repo", "alert_number"}, - }, - - // ── Dependabot ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_dependabot_alerts"), Description: "List Dependabot dependency vulnerability alerts for a repository. Start here for CVE impact on dependencies.", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "State: auto_dismissed, dismissed, fixed, open", "severity": "Severity: low, medium, high, critical", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── Dependabot (extended) ───────────────────────────────────────── - { - Name: mcp.ToolName("github_get_dependabot_alert"), Description: "Get a single Dependabot alert", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "alert_number": "Alert number"}, - Required: []string{"owner", "repo", "alert_number"}, - }, - - // ── Code Scanning (extended) ───────────────────────────────────── - { - Name: mcp.ToolName("github_list_code_scanning_analyses"), Description: "List code scanning SARIF analyses for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "ref": "Git ref to filter by", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── SBOM ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_sbom"), Description: "Get the software bill of materials (SBOM) for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}, - Required: []string{"owner", "repo"}, - }, - - // ── Copilot ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_copilot_org_usage"), Description: "Get Copilot usage metrics for an organization", - Parameters: map[string]string{"org": "Organization name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"org"}, - }, - - // ── Webhooks ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_hooks"), Description: "List webhooks for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - { - Name: mcp.ToolName("github_create_hook"), Description: "Create a webhook for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "url": "Webhook payload URL", "content_type": "Content type: json, form", "events": "Comma-separated events (push, pull_request, issues, etc.)", "active": "Active (true/false, default true)"}, - Required: []string{"owner", "repo", "url"}, - }, - { - Name: mcp.ToolName("github_delete_hook"), Description: "Delete a webhook", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "hook_id": "Webhook ID"}, - Required: []string{"owner", "repo", "hook_id"}, - }, - - // ── Deploy Keys ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_list_deploy_keys"), Description: "List deploy keys for a repository", - Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "page": "Page number", "per_page": "Results per page"}, - Required: []string{"owner", "repo"}, - }, - - // ── Rate Limit ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("github_get_rate_limit"), Description: "Get API rate limit status for the authenticated user", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/github/tools.yaml b/integrations/github/tools.yaml new file mode 100644 index 00000000..4756cea1 --- /dev/null +++ b/integrations/github/tools.yaml @@ -0,0 +1,2722 @@ +version: 1 +tools: + github_search_repos: + description: "Search GitHub repositories. Start here to find repos by name, topic, or language." + parameters: + query: + description: "Search query" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_repo: + description: "Get a repository by owner/name. Use after search_repos or when you already know the owner/repo." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_user_repos: + description: "List repositories for a user. Use when you know the username; prefer search_repos for keyword discovery." + parameters: + username: + description: "GitHub username" + required: true + type: + description: "Type: all, owner, member (default: owner)" + sort: + description: "Sort: created, updated, pushed, full_name" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_org_repos: + description: "List repositories for an organization. Use when you know the org; prefer search_repos for keyword discovery." + parameters: + org: + description: "Organization name" + required: true + type: + description: "Type: all, public, private, forks, sources, member" + sort: + description: "Sort: created, updated, pushed, full_name" + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_repo: + description: "Create a repository for the authenticated user or an org" + parameters: + name: + description: "Repository name" + required: true + org: + description: "Organization (omit for user repo)" + description: + description: "Description" + private: + description: "Private repo (true/false)" + auto_init: + description: "Initialize with README (true/false)" + github_delete_repo: + description: "Delete a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_branches: + description: "List branches of a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_branch: + description: "Get a specific branch" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Branch name" + required: true + github_list_tags: + description: "List tags of a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_contributors: + description: "List contributors to a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_languages: + description: "List languages used in a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_topics: + description: "List repository topics" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_get_readme: + description: "Get the README for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref (branch/tag/sha)" + github_get_file_contents: + description: "Get file or directory contents from a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + path: + description: "File path" + required: true + ref: + description: "Git ref (branch/tag/sha)" + github_create_update_file: + description: "Create or update a file in a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + path: + description: "File path" + required: true + message: + description: "Commit message" + required: true + content: + description: "Raw file content (plain text, not base64 — encoding is handled automatically)" + required: true + sha: + description: "SHA of file being replaced (required for update)" + branch: + description: "Target branch" + github_delete_file: + description: "Delete a file from a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + path: + description: "File path" + required: true + message: + description: "Commit message" + required: true + sha: + description: "SHA of file to delete" + required: true + branch: + description: "Target branch" + github_list_forks: + description: "List forks of a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sort: + description: "Sort: newest, oldest, stargazers, watchers" + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_fork: + description: "Fork a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + organization: + description: "Organization to fork into" + github_list_collaborators: + description: "List collaborators on a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_commit_activity: + description: "Get the last year of commit activity (weekly)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_repo_teams: + description: "List teams with access to a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_compare_commits: + description: "Compare two commits, branches, or tags. Use to see what changed between refs (commit list and diff). Start here for 'what changed in prod' queries." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + base: + description: "Base ref (branch, tag, or SHA)" + required: true + head: + description: "Head ref (branch, tag, or SHA)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_merge_upstream: + description: "Sync a fork branch with the upstream repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Branch to sync" + required: true + github_list_autolinks: + description: "List autolink references for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + github_edit_repo: + description: "Update a repository's settings (description, visibility, default branch, etc.)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + description: + description: "New description" + homepage: + description: "Homepage URL" + default_branch: + description: "Default branch name" + private: + description: "Private (true/false)" + archived: + description: "Archived (true/false)" + has_issues: + description: "Enable issues (true/false)" + has_projects: + description: "Enable projects (true/false)" + has_wiki: + description: "Enable wiki (true/false)" + github_replace_topics: + description: "Replace all topics on a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + topics: + description: "Comma-separated topic names" + required: true + github_rename_branch: + description: "Rename a branch" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Current branch name" + required: true + new_name: + description: "New branch name" + required: true + github_add_collaborator: + description: "Add a collaborator to a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + username: + description: "User to add" + required: true + permission: + description: "Permission: pull, triage, push, maintain, admin" + github_remove_collaborator: + description: "Remove a collaborator from a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + username: + description: "User to remove" + required: true + github_get_combined_status: + description: "Get the combined commit status for a ref (aggregates all status checks)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref (SHA, branch, or tag)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_statuses: + description: "List commit statuses for a ref" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref (SHA, branch, or tag)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_status: + description: "Create a commit status" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Commit SHA" + required: true + state: + description: "State: error, failure, pending, success" + required: true + target_url: + description: "URL to associate with status" + description: + description: "Short description" + context: + description: "Status context identifier" + github_list_deployments: + description: "List deployments for a repository. Start here for deploy status, recent deploys, and rollout history." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + environment: + description: "Filter by environment" + ref: + description: "Filter by ref" + task: + description: "Filter by task" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_deployment: + description: "Get a single deployment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + deployment_id: + description: "Deployment ID" + required: true + github_create_deployment: + description: "Create a deployment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Ref to deploy (branch, tag, or SHA)" + required: true + task: + description: "Task (default: deploy)" + environment: + description: "Environment name" + description: + description: "Description" + auto_merge: + description: "Auto-merge default branch into ref (true/false)" + github_list_deployment_statuses: + description: "List statuses for a deployment. Use after list_deployments to check deploy progress or failure." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + deployment_id: + description: "Deployment ID" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_deployment_status: + description: "Create a deployment status" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + deployment_id: + description: "Deployment ID" + required: true + state: + description: "State: error, failure, inactive, in_progress, queued, pending, success" + required: true + description: + description: "Description" + log_url: + description: "Log URL" + environment: + description: "Override environment name" + github_list_environments: + description: "List environments for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_environment: + description: "Get a single environment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + environment: + description: "Environment name" + required: true + github_get_branch_protection: + description: "Get branch protection rules" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Branch name" + required: true + github_remove_branch_protection: + description: "Remove branch protection rules" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Branch name" + required: true + github_list_rulesets: + description: "List repository rulesets" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_get_ruleset: + description: "Get a repository ruleset by ID" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ruleset_id: + description: "Ruleset ID" + required: true + github_get_rules_for_branch: + description: "Get active rules that apply to a branch" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + branch: + description: "Branch name" + required: true + github_list_traffic_views: + description: "Get repository traffic page views (last 14 days, push access required)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + per: + description: "Aggregation period: day, week" + github_list_traffic_clones: + description: "Get repository traffic clones (last 14 days, push access required)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + per: + description: "Aggregation period: day, week" + github_list_traffic_referrers: + description: "Get top referral sources for a repository (push access required)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_traffic_paths: + description: "Get popular content paths for a repository (push access required)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_get_community_health: + description: "Get community health metrics for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_dispatch_event: + description: "Trigger a repository dispatch event" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + event_type: + description: "Custom event type string" + required: true + github_merge_branch: + description: "Merge a branch into another (not a PR merge — use github_merge_pull for PRs)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + base: + description: "Branch to merge into" + required: true + head: + description: "Branch to merge from" + required: true + commit_message: + description: "Merge commit message" + github_edit_release: + description: "Update a release" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + release_id: + description: "Release ID" + required: true + tag_name: + description: "New tag name" + name: + description: "New release name" + body: + description: "New release notes" + draft: + description: "Draft (true/false)" + prerelease: + description: "Pre-release (true/false)" + github_generate_release_notes: + description: "Auto-generate release notes content between two tags" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + tag_name: + description: "Tag for the release" + required: true + previous_tag_name: + description: "Previous tag to compare against" + target_commitish: + description: "Branch or SHA to tag" + github_list_commit_comments: + description: "List comments on a specific commit" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Commit SHA" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_commit_comment: + description: "Create a comment on a commit" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Commit SHA" + required: true + body: + description: "Comment body" + required: true + path: + description: "Relative file path" + position: + description: "Line position in the diff" + github_list_issues: + description: "List issues for a repository. Start here for issue workflows when you know the repo. For cross-repo search, use search_issues." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: open, closed, all" + labels: + description: "Comma-separated label names" + sort: + description: "Sort: created, updated, comments" + direction: + description: "Direction: asc, desc" + assignee: + description: "Filter by assignee username" + milestone: + description: "Milestone number" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_issue: + description: "Get a single issue with full details. Use after list_issues or search_issues to drill into a specific issue." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + github_create_issue: + description: "Create an issue. Requires owner, repo, and title at minimum." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + title: + description: "Issue title" + required: true + body: + description: "Issue body (markdown)" + assignees: + description: "Comma-separated assignee usernames" + labels: + description: "Comma-separated label names" + milestone: + description: "Milestone number" + github_update_issue: + description: "Update an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + title: + description: "New title" + body: + description: "New body" + state: + description: "State: open, closed" + assignees: + description: "Comma-separated assignee usernames" + labels: + description: "Comma-separated label names" + milestone: + description: "Milestone number" + github_list_issue_comments: + description: "List comments on an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_issue_comment: + description: "Create a comment on an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + body: + description: "Comment body (markdown)" + required: true + github_list_issue_labels: + description: "List labels on an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + github_add_issue_labels: + description: "Add labels to an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + labels: + description: "Comma-separated label names to add" + required: true + github_remove_issue_label: + description: "Remove a label from an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + label: + description: "Label name to remove" + required: true + github_lock_issue: + description: "Lock an issue conversation" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + lock_reason: + description: "Reason: off-topic, too heated, resolved, spam" + github_unlock_issue: + description: "Unlock an issue conversation" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + github_list_milestones: + description: "List milestones for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: open, closed, all" + sort: + description: "Sort: due_on, completeness" + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_milestone: + description: "Create a milestone" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + title: + description: "Milestone title" + required: true + description: + description: "Description" + due_on: + description: "Due date (ISO 8601 YYYY-MM-DDT00:00:00Z)" + state: + description: "State: open, closed" + github_list_issue_events: + description: "List events on an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_issue_timeline: + description: "List timeline events for an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_assignees: + description: "List available assignees for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_update_issue_comment: + description: "Edit an issue or PR comment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + body: + description: "New comment body (markdown)" + required: true + github_delete_issue_comment: + description: "Delete an issue or PR comment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + github_update_milestone: + description: "Update a milestone" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Milestone number" + required: true + title: + description: "New title" + description: + description: "New description" + state: + description: "State: open, closed" + due_on: + description: "Due date (ISO 8601)" + github_delete_milestone: + description: "Delete a milestone" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Milestone number" + required: true + github_list_labels: + description: "List all labels in a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_label: + description: "Create a label in a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Label name" + required: true + color: + description: "Color hex code (without #)" + required: true + description: + description: "Label description" + github_edit_label: + description: "Update a label" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Current label name" + required: true + new_name: + description: "New label name" + color: + description: "New color hex code (without #)" + description: + description: "New description" + github_delete_label: + description: "Delete a label from a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Label name" + required: true + github_create_issue_reaction: + description: "Add a reaction to an issue (+1, -1, laugh, confused, heart, hooray, rocket, eyes)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + content: + description: "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes" + required: true + github_create_issue_comment_reaction: + description: "Add a reaction to an issue comment" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + content: + description: "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes" + required: true + github_list_issue_reactions: + description: "List reactions on an issue" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + number: + description: "Issue number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_pulls: + description: "List pull requests for a repository. Start here for PR workflows when you know the repo. For cross-repo search, use search_issues with type:pr." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: open, closed, all" + head: + description: "Filter by head user:branch" + base: + description: "Filter by base branch" + sort: + description: "Sort: created, updated, popularity, long-running" + direction: + description: "Direction: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_pull: + description: "Get a single pull request with full details. Use after list_pulls to drill into a specific PR. For the diff, follow up with get_pull_diff." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + github_get_pull_diff: + description: "Get the raw unified diff of a pull request. Use after get_pull for the full code diff." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + github_create_pull: + description: "Create a pull request. Requires head branch and base branch." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + title: + description: "PR title" + required: true + head: + description: "Head branch (or user:branch for cross-repo)" + required: true + base: + description: "Base branch" + required: true + body: + description: "PR body (markdown)" + draft: + description: "Create as draft (true/false)" + github_update_pull: + description: "Update a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + title: + description: "New title" + body: + description: "New body" + state: + description: "State: open, closed" + base: + description: "New base branch" + github_list_pull_commits: + description: "List commits on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_pull_files: + description: "List files changed in a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_pull_reviews: + description: "List reviews on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_pull_review: + description: "Create a review on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + body: + description: "Review body" + event: + description: "Review action: APPROVE, REQUEST_CHANGES, COMMENT" + required: true + github_list_pull_comments: + description: "List review comments on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_pull_comment: + description: "Create a review comment on a pull request diff" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + body: + description: "Comment body" + required: true + commit_id: + description: "SHA of the commit to comment on" + required: true + path: + description: "Relative file path" + required: true + line: + description: "Line number in the diff" + github_get_pull_comment: + description: "Get a single review comment on a pull request by its comment ID" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + github_reply_to_pull_comment: + description: "Reply to an existing review comment thread on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + body: + description: "Reply body" + required: true + comment_id: + description: "ID of the comment to reply to" + required: true + github_update_pull_comment: + description: "Update the body of a review comment on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + body: + description: "New comment body (markdown)" + required: true + github_delete_pull_comment: + description: "Delete a review comment on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + comment_id: + description: "Comment ID" + required: true + github_merge_pull: + description: "Merge a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + commit_message: + description: "Merge commit message" + merge_method: + description: "Method: merge, squash, rebase" + github_list_requested_reviewers: + description: "List requested reviewers on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + github_request_reviewers: + description: "Request reviewers on a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + reviewers: + description: "Comma-separated usernames" + team_reviewers: + description: "Comma-separated team slugs" + github_dismiss_pull_review: + description: "Dismiss a pull request review" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + review_id: + description: "Review ID" + required: true + message: + description: "Dismissal message" + required: true + github_update_pull_branch: + description: "Update a PR branch with the latest changes from the base branch" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + expected_head_sha: + description: "Expected SHA of the PR head (for optimistic locking)" + github_remove_reviewers: + description: "Remove requested reviewers from a pull request" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + pull_number: + description: "Pull request number" + required: true + reviewers: + description: "Comma-separated usernames" + team_reviewers: + description: "Comma-separated team slugs" + github_list_pulls_with_commit: + description: "List pull requests that contain a specific commit" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Commit SHA" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_commit: + description: "Get a commit by SHA including files changed. For comparing two refs, use compare_commits instead." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Commit SHA" + required: true + github_list_commits: + description: "List commits on a branch" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Branch name or SHA to start listing from" + path: + description: "Only commits containing this file path" + author: + description: "GitHub login or email to filter by" + since: + description: "Only commits after this date (ISO 8601)" + until: + description: "Only commits before this date (ISO 8601)" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_ref: + description: "Get a git reference (branch or tag)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Reference (e.g., heads/main, tags/v1.0)" + required: true + github_create_ref: + description: "Create a git reference (branch or tag)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Full reference path (e.g., refs/heads/new-branch)" + required: true + sha: + description: "SHA to point the ref at" + required: true + github_delete_ref: + description: "Delete a git reference" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Reference to delete (e.g., heads/old-branch)" + required: true + github_get_tree: + description: "Get a git tree (directory listing)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + sha: + description: "Tree SHA or branch name" + required: true + recursive: + description: "Recurse into subtrees (true/false)" + github_create_tag: + description: "Create an annotated tag object" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + tag: + description: "Tag name" + required: true + message: + description: "Tag message" + required: true + sha: + description: "SHA of object to tag" + required: true + type: + description: "Object type: commit, tree, blob" + github_get_authenticated_user: + description: "Get the currently authenticated user" + parameters: {} + github_get_user: + description: "Get a user by username" + parameters: + username: + description: "GitHub username" + required: true + github_list_user_followers: + description: "List followers of a user" + parameters: + username: + description: "GitHub username" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_user_following: + description: "List users that a user follows" + parameters: + username: + description: "GitHub username" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_user_keys: + description: "List public SSH keys for a user" + parameters: + username: + description: "GitHub username" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_org: + description: "Get an organization" + parameters: + org: + description: "Organization login name" + required: true + github_list_user_orgs: + description: "List organizations for a user" + parameters: + username: + description: "GitHub username (empty for authenticated user)" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_org_members: + description: "List organization members" + parameters: + org: + description: "Organization name" + required: true + role: + description: "Filter: all, admin, member" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_org_teams: + description: "List teams in an organization" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_team_by_slug: + description: "Get a team by slug" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + github_list_team_members: + description: "List members of a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + role: + description: "Filter: all, member, maintainer" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_team_repos: + description: "List repositories for a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_team: + description: "Create a team in an organization" + parameters: + org: + description: "Organization name" + required: true + name: + description: "Team name" + required: true + description: + description: "Team description" + privacy: + description: "Privacy: secret, closed" + permission: + description: "Default permission: pull, push" + github_edit_team: + description: "Update a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + name: + description: "New team name" + required: true + description: + description: "New description" + privacy: + description: "Privacy: secret, closed" + permission: + description: "Default permission: pull, push" + github_delete_team: + description: "Delete a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + github_add_team_member: + description: "Add or update a user's membership in a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + username: + description: "GitHub username" + required: true + role: + description: "Role: member, maintainer" + github_remove_team_member: + description: "Remove a user from a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + username: + description: "GitHub username" + required: true + github_add_team_repo: + description: "Add a repository to a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + permission: + description: "Permission: pull, triage, push, maintain, admin" + github_remove_team_repo: + description: "Remove a repository from a team" + parameters: + org: + description: "Organization name" + required: true + slug: + description: "Team slug" + required: true + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_pending_org_invitations: + description: "List pending organization invitations" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_outside_collaborators: + description: "List outside collaborators for an organization" + parameters: + org: + description: "Organization name" + required: true + filter: + description: "Filter: 2fa_disabled, all" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_workflows: + description: "List workflows in a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_workflow_runs: + description: "List workflow runs for a repository. Use to check CI status or recent builds." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + workflow_id: + description: "Workflow ID or filename (e.g., ci.yml)" + branch: + description: "Filter by branch" + event: + description: "Filter by event (push, pull_request, etc.)" + status: + description: "Filter: completed, action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, in_progress, queued, requested, waiting, pending" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_workflow_run: + description: "Get a specific workflow run. Use after list_workflow_runs for full run details." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_list_workflow_jobs: + description: "List jobs for a workflow run" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + filter: + description: "Filter: latest, all" + page: + description: "Page number" + per_page: + description: "Results per page" + github_download_workflow_logs: + description: "Get a URL to download workflow run logs" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_rerun_workflow: + description: "Re-run a workflow run" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_cancel_workflow_run: + description: "Cancel a workflow run" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_list_repo_secrets: + description: "List repository Actions secrets (names only, not values)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_artifacts: + description: "List artifacts for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_environment_secrets: + description: "List secrets for an environment (names only)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + environment: + description: "Environment name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_org_secrets: + description: "List organization Actions secrets (names only)" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_trigger_workflow: + description: "Trigger a workflow dispatch event" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + workflow_id: + description: "Workflow filename (e.g., ci.yml)" + required: true + ref: + description: "Git ref to run workflow on (branch or tag)" + required: true + github_rerun_failed_jobs: + description: "Re-run only the failed jobs of a workflow run" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_get_workflow_job: + description: "Get a single workflow job by ID" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + job_id: + description: "Job ID" + required: true + github_get_workflow_job_logs: + description: "Get a URL to download a single job's logs" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + job_id: + description: "Job ID" + required: true + github_delete_workflow_run: + description: "Delete a workflow run" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + run_id: + description: "Workflow run ID" + required: true + github_list_repo_variables: + description: "List repository Actions variables (names and values)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_repo_variable: + description: "Create a repository Actions variable" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Variable name" + required: true + value: + description: "Variable value" + required: true + github_update_repo_variable: + description: "Update a repository Actions variable" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Variable name" + required: true + value: + description: "New variable value" + required: true + github_delete_repo_variable: + description: "Delete a repository Actions variable" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + name: + description: "Variable name" + required: true + github_list_org_variables: + description: "List organization Actions variables (names and values)" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_env_variables: + description: "List environment Actions variables (names and values)" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + environment: + description: "Environment name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_runners: + description: "List self-hosted runners for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_runner_registration_token: + description: "Create a repository self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token command; tokens are valid for about 1 hour." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_org_runners: + description: "List self-hosted runners for an organization" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_org_runner_registration_token: + description: "Create an organization self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token command; tokens are valid for about 1 hour." + parameters: + org: + description: "Organization name" + required: true + github_list_check_runs: + description: "List check runs for a git reference" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref (SHA, branch, or tag)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_check_run: + description: "Get a check run by ID" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + check_run_id: + description: "Check run ID" + required: true + github_list_check_suites: + description: "List check suites for a git reference" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref (SHA, branch, or tag)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_releases: + description: "List releases for a repository. Start here for release history, versioning, and what shipped." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_release: + description: "Get a release by ID" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + release_id: + description: "Release ID" + required: true + github_get_latest_release: + description: "Get the latest release for a repository. Use to find what version is current or what shipped most recently." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_create_release: + description: "Create a release" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + tag_name: + description: "Tag name for the release" + required: true + name: + description: "Release name" + body: + description: "Release notes (markdown)" + draft: + description: "Create as draft (true/false)" + prerelease: + description: "Mark as pre-release (true/false)" + target_commitish: + description: "Branch or SHA to tag (defaults to default branch)" + github_delete_release: + description: "Delete a release" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + release_id: + description: "Release ID" + required: true + github_list_release_assets: + description: "List assets for a release" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + release_id: + description: "Release ID" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_gists: + description: "List gists for the authenticated user or a specific user" + parameters: + username: + description: "GitHub username (empty for authenticated user)" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_gist: + description: "Get a gist by ID" + parameters: + id: + description: "Gist ID" + required: true + github_create_gist: + description: "Create a gist" + parameters: + description: + description: "Gist description" + public: + description: "Public gist (true/false)" + filename: + description: "Filename for the gist content" + required: true + content: + description: "File content" + required: true + github_search_topics: + description: "Search GitHub topics" + parameters: + query: + description: "Search query" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_search_labels: + description: "Search labels in a repository" + parameters: + repository_id: + description: "Repository numeric ID" + required: true + query: + description: "Search query" + required: true + sort: + description: "Sort: created, updated" + order: + description: "Order: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_search_code: + description: "Search code across GitHub repositories. Start here to find files, functions, or strings across repos." + parameters: + query: + description: "Search query (supports qualifiers like language:go, repo:owner/name)" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_search_issues: + description: "Search issues and pull requests across GitHub. Start here for cross-repo issue/PR discovery. Add is:pr or is:issue to filter. Use for cross-repo bug, ticket, or PR discovery." + parameters: + query: + description: "Search query (supports qualifiers like is:issue, is:pr, repo:owner/name, state:open)" + required: true + sort: + description: "Sort: comments, reactions, created, updated" + order: + description: "Order: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_search_users: + description: "Search GitHub users" + parameters: + query: + description: "Search query (supports qualifiers like location:, language:, followers:>N)" + required: true + sort: + description: "Sort: followers, repositories, joined" + order: + description: "Order: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_search_commits: + description: "Search commits across GitHub" + parameters: + query: + description: "Search query (supports qualifiers like author:, repo:, committer:)" + required: true + sort: + description: "Sort: author-date, committer-date" + order: + description: "Order: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_stargazers: + description: "List stargazers for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_watchers: + description: "List watchers (subscribers) of a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_notifications: + description: "List notifications for the authenticated user" + parameters: + all: + description: "Show all notifications including read (true/false)" + participating: + description: "Only show participating notifications (true/false)" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_repo_events: + description: "List events for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_mark_notifications_read: + description: "Mark all notifications as read" + parameters: {} + github_star_repo: + description: "Star a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_unstar_repo: + description: "Unstar a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_list_starred: + description: "List repositories starred by a user" + parameters: + username: + description: "GitHub username (empty for authenticated user)" + sort: + description: "Sort: created, updated" + direction: + description: "Direction: asc, desc" + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_code_scanning_alerts: + description: "List code scanning (SAST) alerts for a repository. Start here for security vulnerabilities found by CodeQL or other analyzers." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: open, closed, dismissed, fixed" + ref: + description: "Git ref to filter by" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_code_scanning_alert: + description: "Get a code scanning alert" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + alert_number: + description: "Alert number" + required: true + github_list_secret_scanning_alerts: + description: "List secret scanning alerts for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: open, resolved" + secret_type: + description: "Filter by secret type" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_secret_scanning_alert: + description: "Get a single secret scanning alert" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + alert_number: + description: "Alert number" + required: true + github_list_dependabot_alerts: + description: "List Dependabot dependency vulnerability alerts for a repository. Start here for CVE impact on dependencies." + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + state: + description: "State: auto_dismissed, dismissed, fixed, open" + severity: + description: "Severity: low, medium, high, critical" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_dependabot_alert: + description: "Get a single Dependabot alert" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + alert_number: + description: "Alert number" + required: true + github_list_code_scanning_analyses: + description: "List code scanning SARIF analyses for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + ref: + description: "Git ref to filter by" + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_sbom: + description: "Get the software bill of materials (SBOM) for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + github_get_copilot_org_usage: + description: "Get Copilot usage metrics for an organization" + parameters: + org: + description: "Organization name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_list_hooks: + description: "List webhooks for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_create_hook: + description: "Create a webhook for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + url: + description: "Webhook payload URL" + required: true + content_type: + description: "Content type: json, form" + events: + description: "Comma-separated events (push, pull_request, issues, etc.)" + active: + description: "Active (true/false, default true)" + github_delete_hook: + description: "Delete a webhook" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + hook_id: + description: "Webhook ID" + required: true + github_list_deploy_keys: + description: "List deploy keys for a repository" + parameters: + owner: + description: "Repository owner" + required: true + repo: + description: "Repository name" + required: true + page: + description: "Page number" + per_page: + description: "Results per page" + github_get_rate_limit: + description: "Get API rate limit status for the authenticated user" + parameters: {} diff --git a/integrations/gmail/tools.go b/integrations/gmail/tools.go index 5d0f1847..ce71dc21 100644 --- a/integrations/gmail/tools.go +++ b/integrations/gmail/tools.go @@ -1,292 +1,12 @@ package gmail -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Profile ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_get_profile"), Description: "Get the current user's Gmail profile (email, messages total, threads total, history ID). Start here to verify access.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me' for authenticated user)"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Messages ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_messages"), Description: "List email messages in the user's inbox. Search and find mail using Gmail query syntax.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "q": "Gmail search query (same as Gmail search box)", "label_ids": "Comma-separated label IDs to filter by", "max_results": "Max results per page (default 10, max 500)", "page_token": "Token for next page", "include_spam_trash": "Include SPAM and TRASH (true/false)"}, - }, - { - Name: mcp.ToolName("gmail_get_message"), Description: "Get a specific email message by ID. Read the full mail content, headers, and attachments.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID", "format": "Format: full, metadata, minimal, raw (default full)", "metadata_headers": "Comma-separated headers to include when format=metadata"}, - Required: []string{"message_id"}, - }, - { - Name: mcp.ToolName("gmail_send_message"), Description: "Send an email message. Provide raw RFC 2822 formatted message or use to/subject/body for simple messages", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "from": "Sender email address (defaults to authenticated user)", "to": "Recipient email address(es), comma-separated", "subject": "Email subject", "body": "Email body (plain text)", "raw": "Base64url-encoded RFC 2822 message (overrides from/to/subject/body)", "thread_id": "Thread ID to reply to"}, - }, - { - Name: mcp.ToolName("gmail_delete_message"), Description: "Permanently delete a message (not trash). Cannot be undone", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID"}, - Required: []string{"message_id"}, - }, - { - Name: mcp.ToolName("gmail_trash_message"), Description: "Move a message to the trash", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID"}, - Required: []string{"message_id"}, - }, - { - Name: mcp.ToolName("gmail_untrash_message"), Description: "Remove a message from the trash", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID"}, - Required: []string{"message_id"}, - }, - { - Name: mcp.ToolName("gmail_modify_message"), Description: "Modify labels on a message (add and/or remove labels)", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID", "add_label_ids": "Comma-separated label IDs to add", "remove_label_ids": "Comma-separated label IDs to remove"}, - Required: []string{"message_id"}, - }, - { - Name: mcp.ToolName("gmail_batch_modify"), Description: "Modify labels on multiple messages at once", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_ids": "Comma-separated message IDs", "add_label_ids": "Comma-separated label IDs to add", "remove_label_ids": "Comma-separated label IDs to remove"}, - Required: []string{"message_ids"}, - }, - { - Name: mcp.ToolName("gmail_batch_delete"), Description: "Permanently delete multiple messages. Cannot be undone", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_ids": "Comma-separated message IDs"}, - Required: []string{"message_ids"}, - }, - { - Name: mcp.ToolName("gmail_get_attachment"), Description: "Get a message attachment by ID", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "message_id": "Message ID", "attachment_id": "Attachment ID"}, - Required: []string{"message_id", "attachment_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Threads ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_threads"), Description: "List threads in the user's mailbox", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "q": "Gmail search query", "label_ids": "Comma-separated label IDs to filter by", "max_results": "Max results per page (default 10, max 500)", "page_token": "Token for next page", "include_spam_trash": "Include SPAM and TRASH (true/false)"}, - }, - { - Name: mcp.ToolName("gmail_get_thread"), Description: "Get a specific thread with all its messages", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "thread_id": "Thread ID", "format": "Format: full, metadata, minimal (default full)", "metadata_headers": "Comma-separated headers to include when format=metadata"}, - Required: []string{"thread_id"}, - }, - { - Name: mcp.ToolName("gmail_delete_thread"), Description: "Permanently delete a thread (not trash). Cannot be undone", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "thread_id": "Thread ID"}, - Required: []string{"thread_id"}, - }, - { - Name: mcp.ToolName("gmail_trash_thread"), Description: "Move a thread to the trash", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "thread_id": "Thread ID"}, - Required: []string{"thread_id"}, - }, - { - Name: mcp.ToolName("gmail_untrash_thread"), Description: "Remove a thread from the trash", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "thread_id": "Thread ID"}, - Required: []string{"thread_id"}, - }, - { - Name: mcp.ToolName("gmail_modify_thread"), Description: "Modify labels on a thread (add and/or remove labels)", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "thread_id": "Thread ID", "add_label_ids": "Comma-separated label IDs to add", "remove_label_ids": "Comma-separated label IDs to remove"}, - Required: []string{"thread_id"}, - }, - - // ── Labels ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_labels"), Description: "List all labels in the user's mailbox", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_get_label"), Description: "Get a specific label by ID", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "label_id": "Label ID"}, - Required: []string{"label_id"}, - }, - { - Name: mcp.ToolName("gmail_create_label"), Description: "Create a new label", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "name": "Label name", "message_list_visibility": "Visibility in message list: show, hide", "label_list_visibility": "Visibility in label list: labelShow, labelShowIfUnread, labelHide", "background_color": "Background color hex code", "text_color": "Text color hex code"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gmail_update_label"), Description: "Update a label", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "label_id": "Label ID", "name": "New label name", "message_list_visibility": "Visibility in message list: show, hide", "label_list_visibility": "Visibility in label list: labelShow, labelShowIfUnread, labelHide", "background_color": "Background color hex code", "text_color": "Text color hex code"}, - Required: []string{"label_id"}, - }, - { - Name: mcp.ToolName("gmail_delete_label"), Description: "Permanently delete a label", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "label_id": "Label ID"}, - Required: []string{"label_id"}, - }, - - // ── Drafts ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_drafts"), Description: "List email drafts in the user's mailbox. View unsent composed messages.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "q": "Gmail search query", "max_results": "Max results per page", "page_token": "Token for next page", "include_spam_trash": "Include SPAM and TRASH (true/false)"}, - }, - { - Name: mcp.ToolName("gmail_get_draft"), Description: "Get a specific draft by ID", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "draft_id": "Draft ID", "format": "Format: full, metadata, minimal, raw (default full)"}, - Required: []string{"draft_id"}, - }, - { - Name: mcp.ToolName("gmail_create_draft"), Description: "Create a new email draft. Compose and write a message to send later or save as a reply draft.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "to": "Recipient email address(es), comma-separated", "subject": "Email subject", "body": "Email body (plain text)", "raw": "Base64url-encoded RFC 2822 message (overrides to/subject/body)", "thread_id": "Thread ID for reply drafts"}, - }, - { - Name: mcp.ToolName("gmail_update_draft"), Description: "Update an existing email draft. Edit the composed mail message before sending.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "draft_id": "Draft ID", "to": "Recipient email address(es), comma-separated", "subject": "Email subject", "body": "Email body (plain text)", "raw": "Base64url-encoded RFC 2822 message (overrides to/subject/body)", "thread_id": "Thread ID for reply drafts"}, - Required: []string{"draft_id"}, - }, - { - Name: mcp.ToolName("gmail_delete_draft"), Description: "Permanently delete a draft", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "draft_id": "Draft ID"}, - Required: []string{"draft_id"}, - }, - { - Name: mcp.ToolName("gmail_send_draft"), Description: "Send an existing email draft. Deliver a previously composed mail message.", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "draft_id": "Draft ID"}, - Required: []string{"draft_id"}, - }, - - // ── History ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_history"), Description: "List the history of changes to the mailbox since a given history ID", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "start_history_id": "History ID to start listing from (required)", "label_id": "Filter by label ID", "max_results": "Max results per page", "page_token": "Token for next page", "history_types": "Comma-separated types: messageAdded, messageDeleted, labelAdded, labelRemoved"}, - Required: []string{"start_history_id"}, - }, - - // ── Settings ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_get_vacation"), Description: "Get vacation responder settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_update_vacation"), Description: "Update vacation responder settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "enable_auto_reply": "Enable auto-reply (true/false)", "response_subject": "Auto-reply subject", "response_body_plain_text": "Auto-reply body (plain text)", "response_body_html": "Auto-reply body (HTML)", "restrict_to_contacts": "Only reply to contacts (true/false)", "restrict_to_domain": "Only reply to same domain (true/false)", "start_time": "Start time in milliseconds since epoch", "end_time": "End time in milliseconds since epoch"}, - }, - { - Name: mcp.ToolName("gmail_get_auto_forwarding"), Description: "Get auto-forwarding settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_update_auto_forwarding"), Description: "Update auto-forwarding settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "enabled": "Enable auto-forwarding (true/false)", "email_address": "Email address to forward to", "disposition": "What to do with forwarded messages: leaveInInbox, archive, trash, markRead"}, - }, - { - Name: mcp.ToolName("gmail_get_imap"), Description: "Get IMAP settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_update_imap"), Description: "Update IMAP settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "enabled": "Enable IMAP (true/false)", "auto_expunge": "Auto-expunge (true/false)", "expunge_behavior": "Expunge behavior: archive, deleteForever, trash", "max_folder_size": "Max folder size (0 for no limit)"}, - }, - { - Name: mcp.ToolName("gmail_get_pop"), Description: "Get POP settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_update_pop"), Description: "Update POP settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "access_window": "Access window: disabled, allMail, fromNowOn", "disposition": "What to do after POP fetch: leaveInInbox, archive, trash, markRead"}, - }, - { - Name: mcp.ToolName("gmail_get_language"), Description: "Get language settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_update_language"), Description: "Update language settings", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "display_language": "Display language code (e.g. en, fr, de)"}, - Required: []string{"display_language"}, - }, - - // ── Filters ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_filters"), Description: "List all message filters", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_get_filter"), Description: "Get a specific message filter", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "filter_id": "Filter ID"}, - Required: []string{"filter_id"}, - }, - { - Name: mcp.ToolName("gmail_create_filter"), Description: "Create a new message filter", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "criteria": "JSON string of filter criteria (from, to, subject, query, negatedQuery, hasAttachment, excludeChats, size, sizeComparison)", "action": "JSON string of filter action (addLabelIds, removeLabelIds, forward, sizeComparison)"}, - Required: []string{"criteria", "action"}, - }, - { - Name: mcp.ToolName("gmail_delete_filter"), Description: "Delete a message filter", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "filter_id": "Filter ID"}, - Required: []string{"filter_id"}, - }, - - // ── Forwarding Addresses ──────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_forwarding_addresses"), Description: "List all forwarding addresses", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_get_forwarding_address"), Description: "Get a specific forwarding address", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "forwarding_email": "Forwarding email address"}, - Required: []string{"forwarding_email"}, - }, - { - Name: mcp.ToolName("gmail_create_forwarding_address"), Description: "Create a forwarding address (requires verification)", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "forwarding_email": "Email address to add as forwarding address"}, - Required: []string{"forwarding_email"}, - }, - { - Name: mcp.ToolName("gmail_delete_forwarding_address"), Description: "Delete a forwarding address", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "forwarding_email": "Forwarding email address to remove"}, - Required: []string{"forwarding_email"}, - }, - - // ── Send As ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_send_as"), Description: "List send-as aliases", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_get_send_as"), Description: "Get a specific send-as alias", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "send_as_email": "Send-as email address"}, - Required: []string{"send_as_email"}, - }, - { - Name: mcp.ToolName("gmail_create_send_as"), Description: "Create a custom 'from' send-as alias", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "send_as_email": "Email address for the alias", "display_name": "Display name for the alias", "reply_to_address": "Reply-to address", "is_default": "Set as default send-as (true/false)"}, - Required: []string{"send_as_email"}, - }, - { - Name: mcp.ToolName("gmail_update_send_as"), Description: "Update a send-as alias", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "send_as_email": "Send-as email address", "display_name": "Display name", "reply_to_address": "Reply-to address", "is_default": "Set as default (true/false)"}, - Required: []string{"send_as_email"}, - }, - { - Name: mcp.ToolName("gmail_delete_send_as"), Description: "Delete a send-as alias", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "send_as_email": "Send-as email address to delete"}, - Required: []string{"send_as_email"}, - }, - { - Name: mcp.ToolName("gmail_verify_send_as"), Description: "Send a verification email to a send-as alias address", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "send_as_email": "Send-as email address to verify"}, - Required: []string{"send_as_email"}, - }, - - // ── Delegates ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("gmail_list_delegates"), Description: "List delegates for the account", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')"}, - }, - { - Name: mcp.ToolName("gmail_get_delegate"), Description: "Get a specific delegate", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "delegate_email": "Delegate email address"}, - Required: []string{"delegate_email"}, - }, - { - Name: mcp.ToolName("gmail_create_delegate"), Description: "Add a delegate with verification status set to accepted", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "delegate_email": "Email address of the delegate"}, - Required: []string{"delegate_email"}, - }, - { - Name: mcp.ToolName("gmail_delete_delegate"), Description: "Remove a delegate", - Parameters: map[string]string{"user_id": "User ID (defaults to 'me')", "delegate_email": "Delegate email address to remove"}, - Required: []string{"delegate_email"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gmail/tools.yaml b/integrations/gmail/tools.yaml new file mode 100644 index 00000000..bd23d9f1 --- /dev/null +++ b/integrations/gmail/tools.yaml @@ -0,0 +1,614 @@ +version: 1 +tools: + gmail_get_profile: + description: "Get the current user's Gmail profile (email, messages total, threads total, history ID). Start here to verify access." + parameters: + user_id: + description: "User ID (defaults to 'me' for authenticated user)" + + gmail_list_messages: + description: "List email messages in the user's inbox. Search and find mail using Gmail query syntax." + parameters: + user_id: + description: "User ID (defaults to 'me')" + q: + description: "Gmail search query (same as Gmail search box)" + label_ids: + description: "Comma-separated label IDs to filter by" + max_results: + description: "Max results per page (default 10, max 500)" + page_token: + description: "Token for next page" + include_spam_trash: + description: "Include SPAM and TRASH (true/false)" + + gmail_get_message: + description: "Get a specific email message by ID. Read the full mail content, headers, and attachments." + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + format: + description: "Format: full, metadata, minimal, raw (default full)" + metadata_headers: + description: "Comma-separated headers to include when format=metadata" + + gmail_send_message: + description: "Send an email message. Provide raw RFC 2822 formatted message or use to/subject/body for simple messages" + parameters: + user_id: + description: "User ID (defaults to 'me')" + from: + description: "Sender email address (defaults to authenticated user)" + to: + description: "Recipient email address(es), comma-separated" + subject: + description: "Email subject" + body: + description: "Email body (plain text)" + raw: + description: "Base64url-encoded RFC 2822 message (overrides from/to/subject/body)" + thread_id: + description: "Thread ID to reply to" + + gmail_delete_message: + description: "Permanently delete a message (not trash). Cannot be undone" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + + gmail_trash_message: + description: "Move a message to the trash" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + + gmail_untrash_message: + description: "Remove a message from the trash" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + + gmail_modify_message: + description: "Modify labels on a message (add and/or remove labels)" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + add_label_ids: + description: "Comma-separated label IDs to add" + remove_label_ids: + description: "Comma-separated label IDs to remove" + + gmail_batch_modify: + description: "Modify labels on multiple messages at once" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_ids: + description: "Comma-separated message IDs" + required: true + add_label_ids: + description: "Comma-separated label IDs to add" + remove_label_ids: + description: "Comma-separated label IDs to remove" + + gmail_batch_delete: + description: "Permanently delete multiple messages. Cannot be undone" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_ids: + description: "Comma-separated message IDs" + required: true + + gmail_get_attachment: + description: "Get a message attachment by ID" + parameters: + user_id: + description: "User ID (defaults to 'me')" + message_id: + description: "Message ID" + required: true + attachment_id: + description: "Attachment ID" + required: true + + gmail_list_threads: + description: "List threads in the user's mailbox" + parameters: + user_id: + description: "User ID (defaults to 'me')" + q: + description: "Gmail search query" + label_ids: + description: "Comma-separated label IDs to filter by" + max_results: + description: "Max results per page (default 10, max 500)" + page_token: + description: "Token for next page" + include_spam_trash: + description: "Include SPAM and TRASH (true/false)" + + gmail_get_thread: + description: "Get a specific thread with all its messages" + parameters: + user_id: + description: "User ID (defaults to 'me')" + thread_id: + description: "Thread ID" + required: true + format: + description: "Format: full, metadata, minimal (default full)" + metadata_headers: + description: "Comma-separated headers to include when format=metadata" + + gmail_delete_thread: + description: "Permanently delete a thread (not trash). Cannot be undone" + parameters: + user_id: + description: "User ID (defaults to 'me')" + thread_id: + description: "Thread ID" + required: true + + gmail_trash_thread: + description: "Move a thread to the trash" + parameters: + user_id: + description: "User ID (defaults to 'me')" + thread_id: + description: "Thread ID" + required: true + + gmail_untrash_thread: + description: "Remove a thread from the trash" + parameters: + user_id: + description: "User ID (defaults to 'me')" + thread_id: + description: "Thread ID" + required: true + + gmail_modify_thread: + description: "Modify labels on a thread (add and/or remove labels)" + parameters: + user_id: + description: "User ID (defaults to 'me')" + thread_id: + description: "Thread ID" + required: true + add_label_ids: + description: "Comma-separated label IDs to add" + remove_label_ids: + description: "Comma-separated label IDs to remove" + + gmail_list_labels: + description: "List all labels in the user's mailbox" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_get_label: + description: "Get a specific label by ID" + parameters: + user_id: + description: "User ID (defaults to 'me')" + label_id: + description: "Label ID" + required: true + + gmail_create_label: + description: "Create a new label" + parameters: + user_id: + description: "User ID (defaults to 'me')" + name: + description: "Label name" + required: true + message_list_visibility: + description: "Visibility in message list: show, hide" + label_list_visibility: + description: "Visibility in label list: labelShow, labelShowIfUnread, labelHide" + background_color: + description: "Background color hex code" + text_color: + description: "Text color hex code" + + gmail_update_label: + description: "Update a label" + parameters: + user_id: + description: "User ID (defaults to 'me')" + label_id: + description: "Label ID" + required: true + name: + description: "New label name" + message_list_visibility: + description: "Visibility in message list: show, hide" + label_list_visibility: + description: "Visibility in label list: labelShow, labelShowIfUnread, labelHide" + background_color: + description: "Background color hex code" + text_color: + description: "Text color hex code" + + gmail_delete_label: + description: "Permanently delete a label" + parameters: + user_id: + description: "User ID (defaults to 'me')" + label_id: + description: "Label ID" + required: true + + gmail_list_drafts: + description: "List email drafts in the user's mailbox. View unsent composed messages." + parameters: + user_id: + description: "User ID (defaults to 'me')" + q: + description: "Gmail search query" + max_results: + description: "Max results per page" + page_token: + description: "Token for next page" + include_spam_trash: + description: "Include SPAM and TRASH (true/false)" + + gmail_get_draft: + description: "Get a specific draft by ID" + parameters: + user_id: + description: "User ID (defaults to 'me')" + draft_id: + description: "Draft ID" + required: true + format: + description: "Format: full, metadata, minimal, raw (default full)" + + gmail_create_draft: + description: "Create a new email draft. Compose and write a message to send later or save as a reply draft." + parameters: + user_id: + description: "User ID (defaults to 'me')" + to: + description: "Recipient email address(es), comma-separated" + subject: + description: "Email subject" + body: + description: "Email body (plain text)" + raw: + description: "Base64url-encoded RFC 2822 message (overrides to/subject/body)" + thread_id: + description: "Thread ID for reply drafts" + + gmail_update_draft: + description: "Update an existing email draft. Edit the composed mail message before sending." + parameters: + user_id: + description: "User ID (defaults to 'me')" + draft_id: + description: "Draft ID" + required: true + to: + description: "Recipient email address(es), comma-separated" + subject: + description: "Email subject" + body: + description: "Email body (plain text)" + raw: + description: "Base64url-encoded RFC 2822 message (overrides to/subject/body)" + thread_id: + description: "Thread ID for reply drafts" + + gmail_delete_draft: + description: "Permanently delete a draft" + parameters: + user_id: + description: "User ID (defaults to 'me')" + draft_id: + description: "Draft ID" + required: true + + gmail_send_draft: + description: "Send an existing email draft. Deliver a previously composed mail message." + parameters: + user_id: + description: "User ID (defaults to 'me')" + draft_id: + description: "Draft ID" + required: true + + gmail_list_history: + description: "List the history of changes to the mailbox since a given history ID" + parameters: + user_id: + description: "User ID (defaults to 'me')" + start_history_id: + description: "History ID to start listing from (required)" + required: true + label_id: + description: "Filter by label ID" + max_results: + description: "Max results per page" + page_token: + description: "Token for next page" + history_types: + description: "Comma-separated types: messageAdded, messageDeleted, labelAdded, labelRemoved" + + gmail_get_vacation: + description: "Get vacation responder settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_update_vacation: + description: "Update vacation responder settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + enable_auto_reply: + description: "Enable auto-reply (true/false)" + response_subject: + description: "Auto-reply subject" + response_body_plain_text: + description: "Auto-reply body (plain text)" + response_body_html: + description: "Auto-reply body (HTML)" + restrict_to_contacts: + description: "Only reply to contacts (true/false)" + restrict_to_domain: + description: "Only reply to same domain (true/false)" + start_time: + description: "Start time in milliseconds since epoch" + end_time: + description: "End time in milliseconds since epoch" + + gmail_get_auto_forwarding: + description: "Get auto-forwarding settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_update_auto_forwarding: + description: "Update auto-forwarding settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + enabled: + description: "Enable auto-forwarding (true/false)" + email_address: + description: "Email address to forward to" + disposition: + description: "What to do with forwarded messages: leaveInInbox, archive, trash, markRead" + + gmail_get_imap: + description: "Get IMAP settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_update_imap: + description: "Update IMAP settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + enabled: + description: "Enable IMAP (true/false)" + auto_expunge: + description: "Auto-expunge (true/false)" + expunge_behavior: + description: "Expunge behavior: archive, deleteForever, trash" + max_folder_size: + description: "Max folder size (0 for no limit)" + + gmail_get_pop: + description: "Get POP settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_update_pop: + description: "Update POP settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + access_window: + description: "Access window: disabled, allMail, fromNowOn" + disposition: + description: "What to do after POP fetch: leaveInInbox, archive, trash, markRead" + + gmail_get_language: + description: "Get language settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_update_language: + description: "Update language settings" + parameters: + user_id: + description: "User ID (defaults to 'me')" + display_language: + description: "Display language code (e.g. en, fr, de)" + required: true + + gmail_list_filters: + description: "List all message filters" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_get_filter: + description: "Get a specific message filter" + parameters: + user_id: + description: "User ID (defaults to 'me')" + filter_id: + description: "Filter ID" + required: true + + gmail_create_filter: + description: "Create a new message filter" + parameters: + user_id: + description: "User ID (defaults to 'me')" + criteria: + description: "JSON string of filter criteria (from, to, subject, query, negatedQuery, hasAttachment, excludeChats, size, sizeComparison)" + required: true + action: + description: "JSON string of filter action (addLabelIds, removeLabelIds, forward, sizeComparison)" + required: true + + gmail_delete_filter: + description: "Delete a message filter" + parameters: + user_id: + description: "User ID (defaults to 'me')" + filter_id: + description: "Filter ID" + required: true + + gmail_list_forwarding_addresses: + description: "List all forwarding addresses" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_get_forwarding_address: + description: "Get a specific forwarding address" + parameters: + user_id: + description: "User ID (defaults to 'me')" + forwarding_email: + description: "Forwarding email address" + required: true + + gmail_create_forwarding_address: + description: "Create a forwarding address (requires verification)" + parameters: + user_id: + description: "User ID (defaults to 'me')" + forwarding_email: + description: "Email address to add as forwarding address" + required: true + + gmail_delete_forwarding_address: + description: "Delete a forwarding address" + parameters: + user_id: + description: "User ID (defaults to 'me')" + forwarding_email: + description: "Forwarding email address to remove" + required: true + + gmail_list_send_as: + description: "List send-as aliases" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_get_send_as: + description: "Get a specific send-as alias" + parameters: + user_id: + description: "User ID (defaults to 'me')" + send_as_email: + description: "Send-as email address" + required: true + + gmail_create_send_as: + description: "Create a custom 'from' send-as alias" + parameters: + user_id: + description: "User ID (defaults to 'me')" + send_as_email: + description: "Email address for the alias" + required: true + display_name: + description: "Display name for the alias" + reply_to_address: + description: "Reply-to address" + is_default: + description: "Set as default send-as (true/false)" + + gmail_update_send_as: + description: "Update a send-as alias" + parameters: + user_id: + description: "User ID (defaults to 'me')" + send_as_email: + description: "Send-as email address" + required: true + display_name: + description: "Display name" + reply_to_address: + description: "Reply-to address" + is_default: + description: "Set as default (true/false)" + + gmail_delete_send_as: + description: "Delete a send-as alias" + parameters: + user_id: + description: "User ID (defaults to 'me')" + send_as_email: + description: "Send-as email address to delete" + required: true + + gmail_verify_send_as: + description: "Send a verification email to a send-as alias address" + parameters: + user_id: + description: "User ID (defaults to 'me')" + send_as_email: + description: "Send-as email address to verify" + required: true + + gmail_list_delegates: + description: "List delegates for the account" + parameters: + user_id: + description: "User ID (defaults to 'me')" + + gmail_get_delegate: + description: "Get a specific delegate" + parameters: + user_id: + description: "User ID (defaults to 'me')" + delegate_email: + description: "Delegate email address" + required: true + + gmail_create_delegate: + description: "Add a delegate with verification status set to accepted" + parameters: + user_id: + description: "User ID (defaults to 'me')" + delegate_email: + description: "Email address of the delegate" + required: true + + gmail_delete_delegate: + description: "Remove a delegate" + parameters: + user_id: + description: "User ID (defaults to 'me')" + delegate_email: + description: "Delegate email address to remove" + required: true diff --git a/integrations/gmeet/tools.go b/integrations/gmeet/tools.go index e85e3563..cb61765d 100644 --- a/integrations/gmeet/tools.go +++ b/integrations/gmeet/tools.go @@ -1,92 +1,12 @@ package gmeet -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Spaces (meeting rooms) ────────────────────────────────────── - { - Name: mcp.ToolName("gmeet_create_space"), Description: "Create a new Google Meet meeting space (meeting room). Start here for create meet, new meeting, schedule call, video call, conference room, meeting URL. Returns a Space with meetingUri (the join URL to share), meetingCode (the short code like 'abc-defg-hij'), and a stable resource name 'spaces/{id}'. The space is persistent — anyone with the join URL can start a conference at any time. Optionally pass 'config' to control who can join and moderation settings.", - Parameters: map[string]string{ - "config": "Optional SpaceConfig (JSON object or string) controlling join behavior. Common fields: accessType ('OPEN' = anyone with link, 'TRUSTED' = signed-in only, 'RESTRICTED' = invited only — default OPEN), entryPointAccess ('ALL' or 'CREATOR_APP_ONLY'), moderation ('ON'/'OFF'), moderationRestrictions (object). Example: {\"accessType\":\"TRUSTED\",\"moderation\":\"ON\"}", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gmeet_get_space"), Description: "Retrieve a Google Meet space by resource name or meeting code. Accepts the full 'spaces/{id}', a bare id, or a meeting code like 'abc-defg-hij'. Returns the Space including meetingUri, meetingCode, config, and (if a conference is currently in progress) activeConference.conferenceRecord — the conference record id you can use to look up live participants.", - Parameters: map[string]string{ - "name": "Space resource name ('spaces/{id}'), bare id, or meeting code ('abc-defg-hij'). Meeting codes are case-insensitive and hyphens are optional.", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gmeet_update_space"), Description: "Update a Meet space's configuration (PATCH). Only fields named in update_mask are replaced. Use to change accessType, moderation, moderationRestrictions, or entryPointAccess on an existing space. Requires the meetings.space.settings OAuth scope.", - Parameters: map[string]string{ - "name": "Space resource name ('spaces/{id}') or bare id", - "space": "Space object (JSON object or string) with the new field values. Only the fields listed in update_mask are applied. Example: {\"config\":{\"accessType\":\"RESTRICTED\",\"moderation\":\"ON\"}}", - "update_mask": "Required FieldMask of paths to update (e.g. 'config.accessType,config.moderation'). Valid: config.accessType, config.entryPointAccess, config.moderation, config.moderationRestrictions.chatRestriction, config.moderationRestrictions.reactionRestriction, config.moderationRestrictions.presentRestriction, config.moderationRestrictions.defaultJoinAsViewerType.", - }, - Required: []string{"name", "space", "update_mask"}, - }, - { - Name: mcp.ToolName("gmeet_end_active_conference"), Description: "End the conference currently in progress in a Meet space. All participants are disconnected. The space itself persists — anyone with the join link can start a new conference later. No-op (succeeds with empty body) if no conference is active.", - Parameters: map[string]string{ - "name": "Space resource name ('spaces/{id}') or bare id", - }, - Required: []string{"name"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Conference records (past meetings) ────────────────────────── - { - Name: mcp.ToolName("gmeet_list_conference_records"), Description: "List past Google Meet conferences (conference records) the user attended or owns. Use for queries like 'recent meetings', 'past calls', 'meetings I joined last week'. Returns each conference record with name, the space it was held in, startTime, and endTime. Supports an EBNF filter for date ranges, space, etc.", - Parameters: map[string]string{ - "filter": "Optional EBNF filter expression. Examples: 'space.meeting_code = \"abc-mnop-xyz\"', 'space.name = \"spaces/{spaceId}\"', 'start_time>=\"2024-01-01T00:00:00Z\" AND end_time<=\"2024-02-01T00:00:00Z\"'.", - "page_size": "Optional page size (1-50, default 10)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gmeet_get_conference_record"), Description: "Retrieve a single past conference (conference record) by name. Use when you already have a conference record id from a previous list call or from a space's activeConference.", - Parameters: map[string]string{ - "name": "Conference record resource name ('conferenceRecords/{id}') or bare id", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("gmeet_list_participants"), Description: "List participants who attended a past conference. Returns each participant with their identity (signedinUser.displayName + user.email when available; anonymous attendees are reported separately), earliestStartTime, and latestEndTime. Useful for 'who was in the meeting' / 'attendee list' queries.", - Parameters: map[string]string{ - "conference_record": "Conference record resource name ('conferenceRecords/{id}') or bare id", - "filter": "Optional filter (e.g. 'earliest_start_time>=\"2024-01-01T00:00:00Z\"')", - "page_size": "Optional page size (1-100, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{"conference_record"}, - }, - { - Name: mcp.ToolName("gmeet_list_recordings"), Description: "List video recordings for a past conference. Each recording links to a Google Drive file (driveDestination.file) and has a state (FILE_GENERATED = ready to view, STARTED = still recording, ENDED = stopped). Most conferences have at most one recording.", - Parameters: map[string]string{ - "conference_record": "Conference record resource name ('conferenceRecords/{id}') or bare id", - "page_size": "Optional page size (1-10, default 10)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{"conference_record"}, - }, - { - Name: mcp.ToolName("gmeet_list_transcripts"), Description: "List captions transcripts for a past conference. Each transcript is delivered as a Google Doc (docsDestination.document). Use gmeet_list_transcript_entries to retrieve individual spoken segments from a transcript without downloading the Doc.", - Parameters: map[string]string{ - "conference_record": "Conference record resource name ('conferenceRecords/{id}') or bare id", - "page_size": "Optional page size (1-10, default 10)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{"conference_record"}, - }, - { - Name: mcp.ToolName("gmeet_list_transcript_entries"), Description: "List individual transcript entries (spoken segments) from a conference transcript. Each entry has the participant who spoke, the text they said, languageCode, startTime, and endTime — useful for searching what was said in a meeting without parsing the Doc. Order is chronological.", - Parameters: map[string]string{ - "transcript": "Transcript resource name ('conferenceRecords/{cid}/transcripts/{tid}'). Get it from gmeet_list_transcripts.", - "page_size": "Optional page size (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{"transcript"}, - }, -} +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gmeet/tools.yaml b/integrations/gmeet/tools.yaml new file mode 100644 index 00000000..5b523c6c --- /dev/null +++ b/integrations/gmeet/tools.yaml @@ -0,0 +1,88 @@ +version: 1 +tools: + gmeet_create_space: + description: "Create a new Google Meet meeting space (meeting room). Start here for create meet, new meeting, schedule call, video call, conference room, meeting URL. Returns a Space with meetingUri (the join URL to share), meetingCode (the short code like 'abc-defg-hij'), and a stable resource name 'spaces/{id}'. The space is persistent — anyone with the join URL can start a conference at any time. Optionally pass 'config' to control who can join and moderation settings." + parameters: + config: + description: "Optional SpaceConfig (JSON object or string) controlling join behavior. Common fields: accessType ('OPEN' = anyone with link, 'TRUSTED' = signed-in only, 'RESTRICTED' = invited only — default OPEN), entryPointAccess ('ALL' or 'CREATOR_APP_ONLY'), moderation ('ON'/'OFF'), moderationRestrictions (object). Example: {\"accessType\":\"TRUSTED\",\"moderation\":\"ON\"}" + gmeet_get_space: + description: "Retrieve a Google Meet space by resource name or meeting code. Accepts the full 'spaces/{id}', a bare id, or a meeting code like 'abc-defg-hij'. Returns the Space including meetingUri, meetingCode, config, and (if a conference is currently in progress) activeConference.conferenceRecord — the conference record id you can use to look up live participants." + parameters: + name: + description: "Space resource name ('spaces/{id}'), bare id, or meeting code ('abc-defg-hij'). Meeting codes are case-insensitive and hyphens are optional." + required: true + gmeet_update_space: + description: "Update a Meet space's configuration (PATCH). Only fields named in update_mask are replaced. Use to change accessType, moderation, moderationRestrictions, or entryPointAccess on an existing space. Requires the meetings.space.settings OAuth scope." + parameters: + name: + description: "Space resource name ('spaces/{id}') or bare id" + required: true + space: + description: 'Space object (JSON object or string) with the new field values. Only the fields listed in update_mask are applied. Example: {"config":{"accessType":"RESTRICTED","moderation":"ON"}}' + required: true + update_mask: + description: "Required FieldMask of paths to update (e.g. 'config.accessType,config.moderation'). Valid: config.accessType, config.entryPointAccess, config.moderation, config.moderationRestrictions.chatRestriction, config.moderationRestrictions.reactionRestriction, config.moderationRestrictions.presentRestriction, config.moderationRestrictions.defaultJoinAsViewerType." + required: true + gmeet_end_active_conference: + description: "End the conference currently in progress in a Meet space. All participants are disconnected. The space itself persists — anyone with the join link can start a new conference later. No-op (succeeds with empty body) if no conference is active." + parameters: + name: + description: "Space resource name ('spaces/{id}') or bare id" + required: true + gmeet_list_conference_records: + description: "List past Google Meet conferences (conference records) the user attended or owns. Use for queries like 'recent meetings', 'past calls', 'meetings I joined last week'. Returns each conference record with name, the space it was held in, startTime, and endTime. Supports an EBNF filter for date ranges, space, etc." + parameters: + filter: + description: "Optional EBNF filter expression. Examples: 'space.meeting_code = \"abc-mnop-xyz\"', 'space.name = \"spaces/{spaceId}\"', 'start_time>=\"2024-01-01T00:00:00Z\" AND end_time<=\"2024-02-01T00:00:00Z\"'." + page_size: + description: "Optional page size (1-50, default 10)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gmeet_get_conference_record: + description: "Retrieve a single past conference (conference record) by name. Use when you already have a conference record id from a previous list call or from a space's activeConference." + parameters: + name: + description: "Conference record resource name ('conferenceRecords/{id}') or bare id" + required: true + gmeet_list_participants: + description: "List participants who attended a past conference. Returns each participant with their identity (signedinUser.displayName + user.email when available; anonymous attendees are reported separately), earliestStartTime, and latestEndTime. Useful for 'who was in the meeting' / 'attendee list' queries." + parameters: + conference_record: + description: "Conference record resource name ('conferenceRecords/{id}') or bare id" + required: true + filter: + description: "Optional filter (e.g. 'earliest_start_time>=\"2024-01-01T00:00:00Z\"')" + page_size: + description: "Optional page size (1-100, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gmeet_list_recordings: + description: "List video recordings for a past conference. Each recording links to a Google Drive file (driveDestination.file) and has a state (FILE_GENERATED = ready to view, STARTED = still recording, ENDED = stopped). Most conferences have at most one recording." + parameters: + conference_record: + description: "Conference record resource name ('conferenceRecords/{id}') or bare id" + required: true + page_size: + description: "Optional page size (1-10, default 10)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gmeet_list_transcripts: + description: "List captions transcripts for a past conference. Each transcript is delivered as a Google Doc (docsDestination.document). Use gmeet_list_transcript_entries to retrieve individual spoken segments from a transcript without downloading the Doc." + parameters: + conference_record: + description: "Conference record resource name ('conferenceRecords/{id}') or bare id" + required: true + page_size: + description: "Optional page size (1-10, default 10)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gmeet_list_transcript_entries: + description: "List individual transcript entries (spoken segments) from a conference transcript. Each entry has the participant who spoke, the text they said, languageCode, startTime, and endTime — useful for searching what was said in a meeting without parsing the Doc. Order is chronological." + parameters: + transcript: + description: "Transcript resource name ('conferenceRecords/{cid}/transcripts/{tid}'). Get it from gmeet_list_transcripts." + required: true + page_size: + description: "Optional page size (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" diff --git a/integrations/gpeople/tools.go b/integrations/gpeople/tools.go index 165b2f4e..e5f927ee 100644 --- a/integrations/gpeople/tools.go +++ b/integrations/gpeople/tools.go @@ -1,93 +1,12 @@ package gpeople -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Contacts ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("gpeople_list_contacts"), Description: "List the authenticated user's Google Contacts (address book entries). Start here for contacts, people, address book, phone book, coworkers, emails of people the user knows — paginates through the full /people/me/connections collection. Returns each person with names, email addresses, phone numbers, organizations, and addresses by default; override person_fields for a different shape.", - Parameters: map[string]string{ - "person_fields": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). Valid values: addresses, ageRanges, biographies, birthdays, calendarUrls, clientData, coverPhotos, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, metadata, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, photos, relations, sipAddresses, skills, urls, userDefined.", - "page_size": "Optional page size (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "sort_order": "Optional sort: LAST_MODIFIED_ASCENDING, LAST_MODIFIED_DESCENDING, FIRST_NAME_ASCENDING, or LAST_NAME_ASCENDING (default LAST_MODIFIED_DESCENDING)", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gpeople_search_contacts"), Description: "Search the authenticated user's Google Contacts by name, email, phone, or organization. Use for queries like 'find John', 'who is jane@example.com', 'contacts at Acme'. Returns matched people with names, email addresses, phone numbers, organizations, and addresses by default. The search is across name + email + phone + nickname + organization; partial / prefix matches are supported.", - Parameters: map[string]string{ - "query": "Substring to match against the contact's names/nicknames/emails/phone numbers/organizations (e.g. 'jane', 'acme.com')", - "read_mask": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses'). See gpeople_list_contacts for valid values.", - "page_size": "Optional page size (1-30, default 10). The People API caps searchContacts at 30 results per page.", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("gpeople_get_person"), Description: "Retrieve a single person (contact or directory profile) by resource name. Accepts 'people/c12345', the bare ID 'c12345', or 'me' for the authenticated user's own profile. Use when you already have a resource name from a previous list/search call.", - Parameters: map[string]string{ - "resource_name": "Person resource name (e.g. 'people/c12345'), bare ID, or 'me' for the authenticated user", - "person_fields": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). See gpeople_list_contacts for valid values.", - }, - Required: []string{"resource_name"}, - }, - { - Name: mcp.ToolName("gpeople_create_contact"), Description: "Create a new Google Contact in the authenticated user's address book. Pass a 'person' object containing the field arrays you want set (names, emailAddresses, phoneNumbers, organizations, addresses, biographies, urls, etc.). Returns the new person including its resourceName. Use person_fields to control which fields appear in the response.", - Parameters: map[string]string{ - "person": "Person resource object (JSON object OR JSON string). Example: {\"names\":[{\"givenName\":\"Jane\",\"familyName\":\"Doe\"}],\"emailAddresses\":[{\"value\":\"jane@example.com\",\"type\":\"work\"}],\"phoneNumbers\":[{\"value\":\"+1-555-0100\",\"type\":\"mobile\"}],\"organizations\":[{\"name\":\"Acme\",\"title\":\"Engineer\"}]}", - "person_fields": "Optional comma-separated field mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata').", - }, - Required: []string{"person"}, - }, - { - Name: mcp.ToolName("gpeople_update_contact"), Description: "Update fields on an existing Google Contact. Uses PATCH semantics — only the fields named in update_person_fields are replaced. Requires the contact's etag (from a previous list/get) to detect concurrent modifications; mismatched etags return 400 with reason 'failedPrecondition'.", - Parameters: map[string]string{ - "resource_name": "Person resource name (e.g. 'people/c12345') or bare ID", - "person": "Person object (JSON object OR JSON string) with the new field values and the current etag. The etag MUST be present and match the server's current value. Example: {\"etag\":\"%EgwBAi4...\",\"names\":[{\"givenName\":\"Jane\",\"familyName\":\"Smith\"}],\"emailAddresses\":[{\"value\":\"jane@new.com\",\"type\":\"work\"}]}", - "update_person_fields": "Required comma-separated mask of fields to update (e.g. 'names,emailAddresses'). Valid values: addresses, biographies, birthdays, calendarUrls, clientData, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, relations, sipAddresses, urls, userDefined. NOTE: photos and coverPhotos cannot be updated via this endpoint.", - "person_fields": "Optional comma-separated mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata').", - }, - Required: []string{"resource_name", "person", "update_person_fields"}, - }, - { - Name: mcp.ToolName("gpeople_delete_contact"), Description: "Permanently delete a Google Contact. The contact is removed from the authenticated user's address book and is not recoverable. Returns no content on success.", - Parameters: map[string]string{ - "resource_name": "Person resource name (e.g. 'people/c12345') or bare ID", - }, - Required: []string{"resource_name"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Directory (Google Workspace) ──────────────────────────────── - { - Name: mcp.ToolName("gpeople_list_directory_people"), Description: "List people from the authenticated user's Google Workspace directory (coworkers in the same organization). Requires the user to be on a Google Workspace account with directory access enabled. Returns each person with their name, email, phone, organization, and job title by default.", - Parameters: map[string]string{ - "read_mask": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata'). See gpeople_list_contacts for valid values.", - "sources": "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'). Values: DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT (shared contacts), DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE (organization members).", - "page_size": "Optional page size (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gpeople_search_directory_people"), Description: "Search the authenticated user's Google Workspace directory by name, email, or job title. Use for queries like 'find coworker John', 'who is the VP of Engineering', 'people on the design team'. Requires directory access on a Google Workspace account.", - Parameters: map[string]string{ - "query": "Substring to match against directory people's name/email/job title/department", - "read_mask": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata').", - "sources": "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE').", - "page_size": "Optional page size (1-500, default 30)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{"query"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Other contacts (auto-collected) ───────────────────────────── - { - Name: mcp.ToolName("gpeople_list_other_contacts"), Description: "List the authenticated user's 'Other contacts' — auto-collected contacts (people the user has emailed but never explicitly saved to their address book). Useful for surfacing implicit connections. Read-only.", - Parameters: map[string]string{ - "read_mask": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,metadata'). NOTE: otherContacts only supports a limited set of fields — names, emailAddresses, phoneNumbers, photos, metadata.", - "page_size": "Optional page size (1-1000, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gpeople/tools.yaml b/integrations/gpeople/tools.yaml new file mode 100644 index 00000000..24d1d87b --- /dev/null +++ b/integrations/gpeople/tools.yaml @@ -0,0 +1,93 @@ +version: 1 +tools: + gpeople_list_contacts: + description: "List the authenticated user's Google Contacts (address book entries). Start here for contacts, people, address book, phone book, coworkers, emails of people the user knows — paginates through the full /people/me/connections collection. Returns each person with names, email addresses, phone numbers, organizations, and addresses by default; override person_fields for a different shape." + parameters: + person_fields: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). Valid values: addresses, ageRanges, biographies, birthdays, calendarUrls, clientData, coverPhotos, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, metadata, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, photos, relations, sipAddresses, skills, urls, userDefined." + page_size: + description: "Optional page size (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + sort_order: + description: "Optional sort: LAST_MODIFIED_ASCENDING, LAST_MODIFIED_DESCENDING, FIRST_NAME_ASCENDING, or LAST_NAME_ASCENDING (default LAST_MODIFIED_DESCENDING)" + gpeople_search_contacts: + description: "Search the authenticated user's Google Contacts by name, email, phone, or organization. Use for queries like 'find John', 'who is jane@example.com', 'contacts at Acme'. Returns matched people with names, email addresses, phone numbers, organizations, and addresses by default. The search is across name + email + phone + nickname + organization; partial / prefix matches are supported." + parameters: + query: + description: "Substring to match against the contact's names/nicknames/emails/phone numbers/organizations (e.g. 'jane', 'acme.com')" + required: true + read_mask: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses'). See gpeople_list_contacts for valid values." + page_size: + description: "Optional page size (1-30, default 10). The People API caps searchContacts at 30 results per page." + gpeople_get_person: + description: "Retrieve a single person (contact or directory profile) by resource name. Accepts 'people/c12345', the bare ID 'c12345', or 'me' for the authenticated user's own profile. Use when you already have a resource name from a previous list/search call." + parameters: + resource_name: + description: "Person resource name (e.g. 'people/c12345'), bare ID, or 'me' for the authenticated user" + required: true + person_fields: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). See gpeople_list_contacts for valid values." + gpeople_create_contact: + description: "Create a new Google Contact in the authenticated user's address book. Pass a 'person' object containing the field arrays you want set (names, emailAddresses, phoneNumbers, organizations, addresses, biographies, urls, etc.). Returns the new person including its resourceName. Use person_fields to control which fields appear in the response." + parameters: + person: + description: 'Person resource object (JSON object OR JSON string). Example: {"names":[{"givenName":"Jane","familyName":"Doe"}],"emailAddresses":[{"value":"jane@example.com","type":"work"}],"phoneNumbers":[{"value":"+1-555-0100","type":"mobile"}],"organizations":[{"name":"Acme","title":"Engineer"}]}' + required: true + person_fields: + description: "Optional comma-separated field mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata')." + gpeople_update_contact: + description: "Update fields on an existing Google Contact. Uses PATCH semantics — only the fields named in update_person_fields are replaced. Requires the contact's etag (from a previous list/get) to detect concurrent modifications; mismatched etags return 400 with reason 'failedPrecondition'." + parameters: + resource_name: + description: "Person resource name (e.g. 'people/c12345') or bare ID" + required: true + person: + description: "Person object (JSON object OR JSON string) with the new field values and the current etag. The etag MUST be present and match the server's current value. Example: {\"etag\":\"%EgwBAi4...\",\"names\":[{\"givenName\":\"Jane\",\"familyName\":\"Smith\"}],\"emailAddresses\":[{\"value\":\"jane@new.com\",\"type\":\"work\"}]}" + required: true + update_person_fields: + description: "Required comma-separated mask of fields to update (e.g. 'names,emailAddresses'). Valid values: addresses, biographies, birthdays, calendarUrls, clientData, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, relations, sipAddresses, urls, userDefined. NOTE: photos and coverPhotos cannot be updated via this endpoint." + required: true + person_fields: + description: "Optional comma-separated mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata')." + gpeople_delete_contact: + description: "Permanently delete a Google Contact. The contact is removed from the authenticated user's address book and is not recoverable. Returns no content on success." + parameters: + resource_name: + description: "Person resource name (e.g. 'people/c12345') or bare ID" + required: true + gpeople_list_directory_people: + description: "List people from the authenticated user's Google Workspace directory (coworkers in the same organization). Requires the user to be on a Google Workspace account with directory access enabled. Returns each person with their name, email, phone, organization, and job title by default." + parameters: + read_mask: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata'). See gpeople_list_contacts for valid values." + sources: + description: "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'). Values: DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT (shared contacts), DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE (organization members)." + page_size: + description: "Optional page size (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gpeople_search_directory_people: + description: "Search the authenticated user's Google Workspace directory by name, email, or job title. Use for queries like 'find coworker John', 'who is the VP of Engineering', 'people on the design team'. Requires directory access on a Google Workspace account." + parameters: + query: + description: "Substring to match against directory people's name/email/job title/department" + required: true + read_mask: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata')." + sources: + description: "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE')." + page_size: + description: "Optional page size (1-500, default 30)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gpeople_list_other_contacts: + description: "List the authenticated user's 'Other contacts' — auto-collected contacts (people the user has emailed but never explicitly saved to their address book). Useful for surfacing implicit connections. Read-only." + parameters: + read_mask: + description: "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,metadata'). NOTE: otherContacts only supports a limited set of fields — names, emailAddresses, phoneNumbers, photos, metadata." + page_size: + description: "Optional page size (1-1000, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" diff --git a/integrations/gsheets/tools.go b/integrations/gsheets/tools.go index 221467d0..b836e31c 100644 --- a/integrations/gsheets/tools.go +++ b/integrations/gsheets/tools.go @@ -1,106 +1,12 @@ package gsheets -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Spreadsheet lifecycle ─────────────────────────────────────── - { - Name: mcp.ToolName("gsheets_get_spreadsheet"), Description: "Retrieve (get) a Google Sheets spreadsheet by ID, including its sheet tabs, their properties (titles, grid sizes, hidden state, tab colors), and optional cell data. Start here when you need to discover what sheets/tabs exist, get sheetIds for batchUpdate, or fetch metadata. For raw cell values, use gsheets_get_values (faster and lighter). Set include_grid_data=true to embed cell data — only do this for small sheets, the response can be enormous.", - Parameters: map[string]string{ - "spreadsheet_id": "The Google Sheets spreadsheet ID (the long string in the spreadsheet URL between /d/ and /edit)", - "ranges": "Optional comma-separated A1-notation ranges to limit the response to (e.g. 'Sheet1!A1:C10,Sheet2!A:B')", - "include_grid_data": "true/false — embed full cell grid data in the response (default false; can be huge)", - "fields": "Optional partial-response field mask (e.g. 'sheets.properties,spreadsheetId') to trim the response", - }, - Required: []string{"spreadsheet_id"}, - }, - { - Name: mcp.ToolName("gsheets_create_spreadsheet"), Description: "Create a new Google Sheets spreadsheet. Returns the new spreadsheet including its ID, which you can pass to gsheets_update_values or gsheets_batch_update to add content. To save the spreadsheet into a specific Drive folder, use gdrive_update_file afterward to set parents.", - Parameters: map[string]string{ - "title": "Spreadsheet title (defaults to 'Untitled spreadsheet')", - "sheet_titles": "Optional comma-separated list of initial sheet/tab titles (e.g. 'Summary,Data,Notes'). Default is one sheet named 'Sheet1'.", - }, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Reading values ────────────────────────────────────────────── - { - Name: mcp.ToolName("gsheets_get_values"), Description: "Read cell values from a Google Sheets range using A1 notation. The lightest-weight way to extract data — far smaller than gsheets_get_spreadsheet with grid data. Returns a 2-D array of cell values. Use 'Sheet1' or 'Sheet1!A1:D100'-style ranges. Empty trailing cells are omitted, so each row may have fewer columns than the header row.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "range": "A1-notation range (e.g. 'Sheet1!A1:D100', 'Data', 'Summary!A:C')", - "value_render_option": "How values are returned: FORMATTED_VALUE (default; what's shown in the UI), UNFORMATTED_VALUE (raw values), or FORMULA (cell formulas)", - "date_time_render_option": "How dates/times are returned: SERIAL_NUMBER (default) or FORMATTED_STRING", - "major_dimension": "ROWS (default) or COLUMNS — controls whether the outer array is rows or columns", - }, - Required: []string{"spreadsheet_id", "range"}, - }, - { - Name: mcp.ToolName("gsheets_batch_get_values"), Description: "Read cell values from multiple A1 ranges in one Google Sheets request. Use when you need to pull values from several non-contiguous ranges (e.g. 'Summary!A1:B5' and 'Data!D1:D100') — much faster than multiple gsheets_get_values calls.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "ranges": "Comma-separated A1-notation ranges (e.g. 'Sheet1!A1:B5,Sheet2!C:C')", - "value_render_option": "FORMATTED_VALUE (default), UNFORMATTED_VALUE, or FORMULA", - "date_time_render_option": "SERIAL_NUMBER (default) or FORMATTED_STRING", - "major_dimension": "ROWS (default) or COLUMNS", - }, - Required: []string{"spreadsheet_id", "ranges"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Writing values ────────────────────────────────────────────── - { - Name: mcp.ToolName("gsheets_update_values"), Description: "Overwrite cell values in a Google Sheets range. The values array must match the range shape; cells in the range that aren't present in values are NOT cleared. Pass values as a JSON-encoded 2-D array (rows of columns). For inserting at the bottom of a table, prefer gsheets_append_values.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "range": "A1-notation range to write to (e.g. 'Sheet1!A2:C2')", - "values": "JSON 2-D array of values (e.g. '[[\"a\",\"b\",\"c\"],[1,2,3]]') — strings, numbers, booleans, or null", - "value_input_option": "USER_ENTERED (default — parse like a user typed: formulas evaluate, dates parse) or RAW (store the input verbatim)", - "major_dimension": "ROWS (default) or COLUMNS", - "include_values_in_response": "true/false — return the updated values in the response (default false)", - }, - Required: []string{"spreadsheet_id", "range", "values"}, - }, - { - Name: mcp.ToolName("gsheets_append_values"), Description: "Append rows to the end of a Google Sheets table. Finds the first empty row at or below the given range and inserts there. Use this for log-style or transactional data where you want new rows added without overwriting. Returns the actual range that was updated.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "range": "A1-notation range identifying the table (e.g. 'Sheet1' or 'Sheet1!A1') — Sheets searches downward from the top-left of this range for the next empty row.", - "values": "JSON 2-D array of values (e.g. '[[\"new\",\"row\"]]')", - "value_input_option": "USER_ENTERED (default) or RAW", - "insert_data_option": "OVERWRITE (default — write into existing cells if the table extends) or INSERT_ROWS (shift rows down)", - "major_dimension": "ROWS (default) or COLUMNS", - "include_values_in_response": "true/false — return the appended values (default false)", - }, - Required: []string{"spreadsheet_id", "range", "values"}, - }, - { - Name: mcp.ToolName("gsheets_clear_values"), Description: "Clear the cell values in a Google Sheets range, leaving formatting and other properties intact. Use a full range like 'Sheet1!A:Z' to wipe all data on a tab. To also remove formatting/banding, use gsheets_batch_update with a 'updateCells' request and the relevant field mask.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "range": "A1-notation range to clear (e.g. 'Sheet1!A2:Z')", - }, - Required: []string{"spreadsheet_id", "range"}, - }, - { - Name: mcp.ToolName("gsheets_batch_update_values"), Description: "Update cell values for multiple A1 ranges in one Google Sheets request — a single atomic write covering many ranges. Pass 'data' as a JSON array of {range, values, major_dimension?} objects. Much faster than multiple gsheets_update_values calls.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "data": "JSON array of ValueRange objects (e.g. '[{\"range\":\"Sheet1!A1:B1\",\"values\":[[\"a\",\"b\"]]},{\"range\":\"Sheet2!A1\",\"values\":[[\"hello\"]]}]'). Each entry may include majorDimension.", - "value_input_option": "USER_ENTERED (default) or RAW", - "include_values_in_response": "true/false — return updated values (default false)", - }, - Required: []string{"spreadsheet_id", "data"}, - }, - - // ── Spreadsheet-level batchUpdate (structural changes) ────────── - { - Name: mcp.ToolName("gsheets_batch_update"), Description: "Apply a batch of structural edit requests to a Google Sheets spreadsheet — the full power of the Sheets API. Use for adding/deleting/renaming sheets, formatting cells, inserting columns, freezing panes, conditional formatting, charts, banding, protected ranges, named ranges, and so on. For pure value writes, use gsheets_update_values, gsheets_append_values, or gsheets_batch_update_values instead — those are simpler and don't need sheetIds.", - Parameters: map[string]string{ - "spreadsheet_id": "The spreadsheet ID", - "requests": "JSON array of Sheets API Request objects (e.g. [{\"addSheet\":{\"properties\":{\"title\":\"NewTab\"}}}])", - "include_spreadsheet_in_response": "true/false — include the full spreadsheet in the response (default false)", - "response_include_grid_data": "true/false — include grid data if the spreadsheet is in the response (default false)", - "response_ranges": "Optional comma-separated A1-notation ranges to limit the spreadsheet response to", - }, - Required: []string{"spreadsheet_id", "requests"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gsheets/tools.yaml b/integrations/gsheets/tools.yaml new file mode 100644 index 00000000..c814a050 --- /dev/null +++ b/integrations/gsheets/tools.yaml @@ -0,0 +1,126 @@ +version: 1 +tools: + gsheets_get_spreadsheet: + description: "Retrieve (get) a Google Sheets spreadsheet by ID, including its sheet tabs, their properties (titles, grid sizes, hidden state, tab colors), and optional cell data. Start here when you need to discover what sheets/tabs exist, get sheetIds for batchUpdate, or fetch metadata. For raw cell values, use gsheets_get_values (faster and lighter). Set include_grid_data=true to embed cell data — only do this for small sheets, the response can be enormous." + parameters: + spreadsheet_id: + description: "The Google Sheets spreadsheet ID (the long string in the spreadsheet URL between /d/ and /edit)" + required: true + ranges: + description: "Optional comma-separated A1-notation ranges to limit the response to (e.g. 'Sheet1!A1:C10,Sheet2!A:B')" + include_grid_data: + description: "true/false — embed full cell grid data in the response (default false; can be huge)" + fields: + description: "Optional partial-response field mask (e.g. 'sheets.properties,spreadsheetId') to trim the response" + gsheets_create_spreadsheet: + description: "Create a new Google Sheets spreadsheet. Returns the new spreadsheet including its ID, which you can pass to gsheets_update_values or gsheets_batch_update to add content. To save the spreadsheet into a specific Drive folder, use gdrive_update_file afterward to set parents." + parameters: + title: + description: "Spreadsheet title (defaults to 'Untitled spreadsheet')" + sheet_titles: + description: "Optional comma-separated list of initial sheet/tab titles (e.g. 'Summary,Data,Notes'). Default is one sheet named 'Sheet1'." + gsheets_get_values: + description: "Read cell values from a Google Sheets range using A1 notation. The lightest-weight way to extract data — far smaller than gsheets_get_spreadsheet with grid data. Returns a 2-D array of cell values. Use 'Sheet1' or 'Sheet1!A1:D100'-style ranges. Empty trailing cells are omitted, so each row may have fewer columns than the header row." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + range: + description: "A1-notation range (e.g. 'Sheet1!A1:D100', 'Data', 'Summary!A:C')" + required: true + value_render_option: + description: "How values are returned: FORMATTED_VALUE (default; what's shown in the UI), UNFORMATTED_VALUE (raw values), or FORMULA (cell formulas)" + date_time_render_option: + description: "How dates/times are returned: SERIAL_NUMBER (default) or FORMATTED_STRING" + major_dimension: + description: "ROWS (default) or COLUMNS — controls whether the outer array is rows or columns" + gsheets_batch_get_values: + description: "Read cell values from multiple A1 ranges in one Google Sheets request. Use when you need to pull values from several non-contiguous ranges (e.g. 'Summary!A1:B5' and 'Data!D1:D100') — much faster than multiple gsheets_get_values calls." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + ranges: + description: "Comma-separated A1-notation ranges (e.g. 'Sheet1!A1:B5,Sheet2!C:C')" + required: true + value_render_option: + description: "FORMATTED_VALUE (default), UNFORMATTED_VALUE, or FORMULA" + date_time_render_option: + description: "SERIAL_NUMBER (default) or FORMATTED_STRING" + major_dimension: + description: "ROWS (default) or COLUMNS" + gsheets_update_values: + description: "Overwrite cell values in a Google Sheets range. The values array must match the range shape; cells in the range that aren't present in values are NOT cleared. Pass values as a JSON-encoded 2-D array (rows of columns). For inserting at the bottom of a table, prefer gsheets_append_values." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + range: + description: "A1-notation range to write to (e.g. 'Sheet1!A2:C2')" + required: true + values: + description: "JSON 2-D array of values (e.g. '[[\"a\",\"b\",\"c\"],[1,2,3]]') — strings, numbers, booleans, or null" + required: true + value_input_option: + description: "USER_ENTERED (default — parse like a user typed: formulas evaluate, dates parse) or RAW (store the input verbatim)" + major_dimension: + description: "ROWS (default) or COLUMNS" + include_values_in_response: + description: "true/false — return the updated values in the response (default false)" + gsheets_append_values: + description: "Append rows to the end of a Google Sheets table. Finds the first empty row at or below the given range and inserts there. Use this for log-style or transactional data where you want new rows added without overwriting. Returns the actual range that was updated." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + range: + description: "A1-notation range identifying the table (e.g. 'Sheet1' or 'Sheet1!A1') — Sheets searches downward from the top-left of this range for the next empty row." + required: true + values: + description: "JSON 2-D array of values (e.g. '[[\"new\",\"row\"]]')" + required: true + value_input_option: + description: "USER_ENTERED (default) or RAW" + insert_data_option: + description: "OVERWRITE (default — write into existing cells if the table extends) or INSERT_ROWS (shift rows down)" + major_dimension: + description: "ROWS (default) or COLUMNS" + include_values_in_response: + description: "true/false — return the appended values (default false)" + gsheets_clear_values: + description: "Clear the cell values in a Google Sheets range, leaving formatting and other properties intact. Use a full range like 'Sheet1!A:Z' to wipe all data on a tab. To also remove formatting/banding, use gsheets_batch_update with a 'updateCells' request and the relevant field mask." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + range: + description: "A1-notation range to clear (e.g. 'Sheet1!A2:Z')" + required: true + gsheets_batch_update_values: + description: "Update cell values for multiple A1 ranges in one Google Sheets request — a single atomic write covering many ranges. Pass 'data' as a JSON array of {range, values, major_dimension?} objects. Much faster than multiple gsheets_update_values calls." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + data: + description: "JSON array of ValueRange objects (e.g. '[{\"range\":\"Sheet1!A1:B1\",\"values\":[[\"a\",\"b\"]]},{\"range\":\"Sheet2!A1\",\"values\":[[\"hello\"]]}]'). Each entry may include majorDimension." + required: true + value_input_option: + description: "USER_ENTERED (default) or RAW" + include_values_in_response: + description: "true/false — return updated values (default false)" + gsheets_batch_update: + description: "Apply a batch of structural edit requests to a Google Sheets spreadsheet — the full power of the Sheets API. Use for adding/deleting/renaming sheets, formatting cells, inserting columns, freezing panes, conditional formatting, charts, banding, protected ranges, named ranges, and so on. For pure value writes, use gsheets_update_values, gsheets_append_values, or gsheets_batch_update_values instead — those are simpler and don't need sheetIds." + parameters: + spreadsheet_id: + description: "The spreadsheet ID" + required: true + requests: + description: 'JSON array of Sheets API Request objects (e.g. [{"addSheet":{"properties":{"title":"NewTab"}}}])' + required: true + include_spreadsheet_in_response: + description: "true/false — include the full spreadsheet in the response (default false)" + response_include_grid_data: + description: "true/false — include grid data if the spreadsheet is in the response (default false)" + response_ranges: + description: "Optional comma-separated A1-notation ranges to limit the spreadsheet response to" diff --git a/integrations/gslides/tools.go b/integrations/gslides/tools.go index 8147c603..bc2f5406 100644 --- a/integrations/gslides/tools.go +++ b/integrations/gslides/tools.go @@ -1,52 +1,12 @@ package gslides -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Presentation lifecycle ────────────────────────────────────── - { - Name: mcp.ToolName("gslides_get_presentation"), Description: "Retrieve (get) a Google Slides presentation by ID, including its slides (pages), layouts, masters, and page elements (shapes, text, tables, images). Start here when you need to discover slide IDs for batchUpdate, inspect existing content, or read the textual content of a deck. The response can be very large for content-rich decks — use gslides_get_page for a single slide if you only need one. For thumbnails, use gslides_get_page_thumbnail.", - Parameters: map[string]string{ - "presentation_id": "The Google Slides presentation ID (the long string in the URL between /d/ and /edit)", - "fields": "Optional partial-response field mask (e.g. 'slides.objectId,title') to trim the response", - }, - Required: []string{"presentation_id"}, - }, - { - Name: mcp.ToolName("gslides_create_presentation"), Description: "Create a new Google Slides presentation. Returns the new presentation including its ID and an initial blank slide. Pass the returned presentation_id to gslides_batch_update to add slides and content. To save the presentation into a specific Drive folder, use gdrive_update_file afterward to set parents.", - Parameters: map[string]string{ - "title": "Presentation title (defaults to 'Untitled presentation')", - }, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Page (slide) access ───────────────────────────────────────── - { - Name: mcp.ToolName("gslides_get_page"), Description: "Retrieve a single page (slide) from a Google Slides presentation by its object ID. Much lighter than gslides_get_presentation when you only need one slide's content or page elements. The page_object_id comes from the slides[].objectId field of a presentation response.", - Parameters: map[string]string{ - "presentation_id": "The presentation ID", - "page_object_id": "The object ID of the slide page (from slides[].objectId)", - }, - Required: []string{"presentation_id", "page_object_id"}, - }, - { - Name: mcp.ToolName("gslides_get_page_thumbnail"), Description: "Generate a thumbnail image URL for a single Google Slides page (slide). Returns a short-lived signed URL pointing to a PNG. Use thumbnail_size to control resolution (LARGE/MEDIUM/SMALL or unspecified). The mime_type defaults to PNG.", - Parameters: map[string]string{ - "presentation_id": "The presentation ID", - "page_object_id": "The object ID of the slide page (from slides[].objectId)", - "thumbnail_size": "LARGE / MEDIUM / SMALL (default unspecified = medium)", - "mime_type": "PNG (default) — currently the only supported value", - }, - Required: []string{"presentation_id", "page_object_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Presentation-level batchUpdate (the workhorse) ────────────── - { - Name: mcp.ToolName("gslides_batch_update"), Description: "Apply a batch of edit requests to a Google Slides presentation — the primary mutation API. Use for creating/deleting slides, inserting text, replacing text, formatting, inserting shapes/tables/images, moving page elements, applying themes, and so on. Pass 'requests' as a JSON array of Slides API Request objects (e.g. [{\"createSlide\":{}}, {\"insertText\":{\"objectId\":\"...\",\"text\":\"hello\"}}]). For pure read access, use gslides_get_presentation or gslides_get_page instead.", - Parameters: map[string]string{ - "presentation_id": "The presentation ID", - "requests": "JSON array of Slides API Request objects", - "write_control_revision": "Optional required revision ID for optimistic concurrency control (will fail if the presentation has been edited since this revision)", - }, - Required: []string{"presentation_id", "requests"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gslides/tools.yaml b/integrations/gslides/tools.yaml new file mode 100644 index 00000000..1985cbcd --- /dev/null +++ b/integrations/gslides/tools.yaml @@ -0,0 +1,48 @@ +version: 1 +tools: + gslides_get_presentation: + description: "Retrieve (get) a Google Slides presentation by ID, including its slides (pages), layouts, masters, and page elements (shapes, text, tables, images). Start here when you need to discover slide IDs for batchUpdate, inspect existing content, or read the textual content of a deck. The response can be very large for content-rich decks — use gslides_get_page for a single slide if you only need one. For thumbnails, use gslides_get_page_thumbnail." + parameters: + presentation_id: + description: "The Google Slides presentation ID (the long string in the URL between /d/ and /edit)" + required: true + fields: + description: "Optional partial-response field mask (e.g. 'slides.objectId,title') to trim the response" + gslides_create_presentation: + description: "Create a new Google Slides presentation. Returns the new presentation including its ID and an initial blank slide. Pass the returned presentation_id to gslides_batch_update to add slides and content. To save the presentation into a specific Drive folder, use gdrive_update_file afterward to set parents." + parameters: + title: + description: "Presentation title (defaults to 'Untitled presentation')" + gslides_get_page: + description: "Retrieve a single page (slide) from a Google Slides presentation by its object ID. Much lighter than gslides_get_presentation when you only need one slide's content or page elements. The page_object_id comes from the slides[].objectId field of a presentation response." + parameters: + presentation_id: + description: "The presentation ID" + required: true + page_object_id: + description: "The object ID of the slide page (from slides[].objectId)" + required: true + gslides_get_page_thumbnail: + description: "Generate a thumbnail image URL for a single Google Slides page (slide). Returns a short-lived signed URL pointing to a PNG. Use thumbnail_size to control resolution (LARGE/MEDIUM/SMALL or unspecified). The mime_type defaults to PNG." + parameters: + presentation_id: + description: "The presentation ID" + required: true + page_object_id: + description: "The object ID of the slide page (from slides[].objectId)" + required: true + thumbnail_size: + description: "LARGE / MEDIUM / SMALL (default unspecified = medium)" + mime_type: + description: "PNG (default) — currently the only supported value" + gslides_batch_update: + description: "Apply a batch of edit requests to a Google Slides presentation — the primary mutation API. Use for creating/deleting slides, inserting text, replacing text, formatting, inserting shapes/tables/images, moving page elements, applying themes, and so on. Pass 'requests' as a JSON array of Slides API Request objects (e.g. [{\"createSlide\":{}}, {\"insertText\":{\"objectId\":\"...\",\"text\":\"hello\"}}]). For pure read access, use gslides_get_presentation or gslides_get_page instead." + parameters: + presentation_id: + description: "The presentation ID" + required: true + requests: + description: "JSON array of Slides API Request objects" + required: true + write_control_revision: + description: "Optional required revision ID for optimistic concurrency control (will fail if the presentation has been edited since this revision)" diff --git a/integrations/gtasks/tools.go b/integrations/gtasks/tools.go index ccf7a885..45e96250 100644 --- a/integrations/gtasks/tools.go +++ b/integrations/gtasks/tools.go @@ -1,108 +1,12 @@ package gtasks -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Tasklist resource ─────────────────────────────────────────── - { - Name: mcp.ToolName("gtasks_list_tasklists"), Description: "List the authenticated user's Google Tasks tasklists (todo lists, task lists, todo categories). Start here for todos, to-do items, action items, daily tasks, reminders, checklists — to discover the tasklist IDs needed by every other gtasks tool. The default tasklist (named 'My Tasks' or similar) always exists for any Google account.", - Parameters: map[string]string{ - "max_results": "Optional max tasklists per page (1-100, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - }, - Required: []string{}, - }, - { - Name: mcp.ToolName("gtasks_create_tasklist"), Description: "Create a new Google Tasks tasklist (todo list, task category). Returns the new tasklist including its id.", - Parameters: map[string]string{ - "title": "Tasklist title (shown in the Tasks UI)", - }, - Required: []string{"title"}, - }, - { - Name: mcp.ToolName("gtasks_delete_tasklist"), Description: "Delete a Google Tasks tasklist. Permanently removes the list AND all tasks within it. Cannot delete the default tasklist.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID (from gtasks_list_tasklists)", - }, - Required: []string{"tasklist_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Task resource ─────────────────────────────────────────────── - { - Name: mcp.ToolName("gtasks_list_tasks"), Description: "List tasks (todos, action items) within a Google Tasks tasklist. Returns tasks with their title, notes, due date, status (needsAction/completed), parent, and position. Use to view a user's todo list, find overdue items, or build a daily agenda alongside gcal_list_events. Combine with show_completed=true to include finished tasks.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID (from gtasks_list_tasklists)", - "max_results": "Optional max tasks per page (1-100, default 100)", - "page_token": "Optional pagination token from a previous response's nextPageToken", - "show_completed": "Optional boolean — include completed tasks (default false)", - "show_hidden": "Optional boolean — include hidden tasks (default false)", - "show_deleted": "Optional boolean — include deleted tasks (default false)", - "due_min": "Optional lower bound on due date (RFC 3339 timestamp, e.g. 2024-01-01T00:00:00Z)", - "due_max": "Optional upper bound on due date (RFC 3339 timestamp)", - "completed_min": "Optional lower bound on completion time (RFC 3339 timestamp)", - "completed_max": "Optional upper bound on completion time (RFC 3339 timestamp)", - "updated_min": "Optional lower bound on last-modified time (RFC 3339 timestamp)", - }, - Required: []string{"tasklist_id"}, - }, - { - Name: mcp.ToolName("gtasks_get_task"), Description: "Retrieve a single Google Tasks task by ID. Useful when you already have the task ID from a previous list call.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID", - "task_id": "The task ID (from gtasks_list_tasks)", - }, - Required: []string{"tasklist_id", "task_id"}, - }, - { - Name: mcp.ToolName("gtasks_create_task"), Description: "Create a new task (todo, action item, reminder) in a Google Tasks tasklist. Returns the new task including its id. Use parent_id to make this a subtask of an existing task. Use previous_id to control position within siblings.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID to add the task to", - "title": "Task title", - "notes": "Optional task notes / description", - "due": "Optional due date (RFC 3339 timestamp). Note: the Tasks API only stores the date portion; the time is always 00:00:00Z.", - "parent_id": "Optional parent task ID — makes this a subtask of an existing task", - "previous_id": "Optional sibling task ID — new task will be positioned immediately after this one (omit to insert at top)", - }, - Required: []string{"tasklist_id", "title"}, - }, - { - Name: mcp.ToolName("gtasks_update_task"), Description: "Update fields on an existing Google Tasks task (mark complete, change title, edit notes, set due date). Uses PATCH semantics — only the fields you provide are changed. Set status='completed' to mark a task done; set status='needsAction' to reopen.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID", - "task_id": "The task ID", - "title": "Optional new title", - "notes": "Optional new notes (pass empty string to clear)", - "due": "Optional new due date (RFC 3339 timestamp)", - "status": "Optional new status: 'needsAction' (open) or 'completed' (done)", - "completed": "Optional completion timestamp (RFC 3339); usually set automatically when status='completed'", - "deleted": "Optional boolean — soft-delete the task", - "hidden": "Optional boolean — hide the task from the default view", - }, - Required: []string{"tasklist_id", "task_id"}, - }, - { - Name: mcp.ToolName("gtasks_delete_task"), Description: "Permanently delete a Google Tasks task. To soft-delete instead (so it can be recovered), use gtasks_update_task with deleted=true.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID", - "task_id": "The task ID", - }, - Required: []string{"tasklist_id", "task_id"}, - }, - { - Name: mcp.ToolName("gtasks_move_task"), Description: "Move a Google Tasks task to a new position within its tasklist — reparent (make it a subtask) and/or reorder (place after a sibling). Returns the updated task.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID", - "task_id": "The task ID to move", - "parent_id": "Optional new parent task ID (omit for top-level)", - "previous_id": "Optional sibling ID — task will be positioned immediately after this one (omit to move to top)", - }, - Required: []string{"tasklist_id", "task_id"}, - }, - { - Name: mcp.ToolName("gtasks_clear_completed"), Description: "Clear all completed tasks from a Google Tasks tasklist (hides them from the default view). Returns no content on success. Equivalent to the 'Delete all completed tasks' menu action in the Tasks UI — note: tasks are hidden, not permanently deleted.", - Parameters: map[string]string{ - "tasklist_id": "The tasklist ID", - }, - Required: []string{"tasklist_id"}, - }, -} +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/gtasks/tools.yaml b/integrations/gtasks/tools.yaml new file mode 100644 index 00000000..2d3e3498 --- /dev/null +++ b/integrations/gtasks/tools.yaml @@ -0,0 +1,124 @@ +version: 1 +tools: + gtasks_list_tasklists: + description: "List the authenticated user's Google Tasks tasklists (todo lists, task lists, todo categories). Start here for todos, to-do items, action items, daily tasks, reminders, checklists — to discover the tasklist IDs needed by every other gtasks tool. The default tasklist (named 'My Tasks' or similar) always exists for any Google account." + parameters: + max_results: + description: "Optional max tasklists per page (1-100, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + gtasks_create_tasklist: + description: "Create a new Google Tasks tasklist (todo list, task category). Returns the new tasklist including its id." + parameters: + title: + description: "Tasklist title (shown in the Tasks UI)" + required: true + gtasks_delete_tasklist: + description: "Delete a Google Tasks tasklist. Permanently removes the list AND all tasks within it. Cannot delete the default tasklist." + parameters: + tasklist_id: + description: "The tasklist ID (from gtasks_list_tasklists)" + required: true + gtasks_list_tasks: + description: "List tasks (todos, action items) within a Google Tasks tasklist. Returns tasks with their title, notes, due date, status (needsAction/completed), parent, and position. Use to view a user's todo list, find overdue items, or build a daily agenda alongside gcal_list_events. Combine with show_completed=true to include finished tasks." + parameters: + tasklist_id: + description: "The tasklist ID (from gtasks_list_tasklists)" + required: true + max_results: + description: "Optional max tasks per page (1-100, default 100)" + page_token: + description: "Optional pagination token from a previous response's nextPageToken" + show_completed: + description: "Optional boolean — include completed tasks (default false)" + show_hidden: + description: "Optional boolean — include hidden tasks (default false)" + show_deleted: + description: "Optional boolean — include deleted tasks (default false)" + due_min: + description: "Optional lower bound on due date (RFC 3339 timestamp, e.g. 2024-01-01T00:00:00Z)" + due_max: + description: "Optional upper bound on due date (RFC 3339 timestamp)" + completed_min: + description: "Optional lower bound on completion time (RFC 3339 timestamp)" + completed_max: + description: "Optional upper bound on completion time (RFC 3339 timestamp)" + updated_min: + description: "Optional lower bound on last-modified time (RFC 3339 timestamp)" + gtasks_get_task: + description: "Retrieve a single Google Tasks task by ID. Useful when you already have the task ID from a previous list call." + parameters: + tasklist_id: + description: "The tasklist ID" + required: true + task_id: + description: "The task ID (from gtasks_list_tasks)" + required: true + gtasks_create_task: + description: "Create a new task (todo, action item, reminder) in a Google Tasks tasklist. Returns the new task including its id. Use parent_id to make this a subtask of an existing task. Use previous_id to control position within siblings." + parameters: + tasklist_id: + description: "The tasklist ID to add the task to" + required: true + title: + description: "Task title" + required: true + notes: + description: "Optional task notes / description" + due: + description: "Optional due date (RFC 3339 timestamp). Note: the Tasks API only stores the date portion; the time is always 00:00:00Z." + parent_id: + description: "Optional parent task ID — makes this a subtask of an existing task" + previous_id: + description: "Optional sibling task ID — new task will be positioned immediately after this one (omit to insert at top)" + gtasks_update_task: + description: "Update fields on an existing Google Tasks task (mark complete, change title, edit notes, set due date). Uses PATCH semantics — only the fields you provide are changed. Set status='completed' to mark a task done; set status='needsAction' to reopen." + parameters: + tasklist_id: + description: "The tasklist ID" + required: true + task_id: + description: "The task ID" + required: true + title: + description: "Optional new title" + notes: + description: "Optional new notes (pass empty string to clear)" + due: + description: "Optional new due date (RFC 3339 timestamp)" + status: + description: "Optional new status: 'needsAction' (open) or 'completed' (done)" + completed: + description: "Optional completion timestamp (RFC 3339); usually set automatically when status='completed'" + deleted: + description: "Optional boolean — soft-delete the task" + hidden: + description: "Optional boolean — hide the task from the default view" + gtasks_delete_task: + description: "Permanently delete a Google Tasks task. To soft-delete instead (so it can be recovered), use gtasks_update_task with deleted=true." + parameters: + tasklist_id: + description: "The tasklist ID" + required: true + task_id: + description: "The task ID" + required: true + gtasks_move_task: + description: "Move a Google Tasks task to a new position within its tasklist — reparent (make it a subtask) and/or reorder (place after a sibling). Returns the updated task." + parameters: + tasklist_id: + description: "The tasklist ID" + required: true + task_id: + description: "The task ID to move" + required: true + parent_id: + description: "Optional new parent task ID (omit for top-level)" + previous_id: + description: "Optional sibling ID — task will be positioned immediately after this one (omit to move to top)" + gtasks_clear_completed: + description: "Clear all completed tasks from a Google Tasks tasklist (hides them from the default view). Returns no content on success. Equivalent to the 'Delete all completed tasks' menu action in the Tasks UI — note: tasks are hidden, not permanently deleted." + parameters: + tasklist_id: + description: "The tasklist ID" + required: true diff --git a/integrations/jira/tools.go b/integrations/jira/tools.go index 3aa7c2d3..458fb033 100644 --- a/integrations/jira/tools.go +++ b/integrations/jira/tools.go @@ -1,222 +1,12 @@ package jira -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Issues ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("jira_search_issues"), Description: "Search issues using JQL (Jira Query Language). Start here for most issue workflows. Returns paginated results; pass nextPageToken from response to fetch next page", - Parameters: map[string]string{"jql": "JQL query (e.g., 'project = PROJ AND status = Open')", "fields": "Comma-separated fields to return (default: summary,status,assignee,priority,issuetype)", "next_page_token": "Cursor for next page (from previous response's nextPageToken field)", "max_results": "Max results per page (default 200, server may cap)"}, - Required: []string{"jql"}, - }, - { - Name: mcp.ToolName("jira_get_issue"), Description: "Get full details of a specific issue by key or ID", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123) or ID", "fields": "Comma-separated fields to return (default: all)", "expand": "Comma-separated expansions (e.g., changelog,renderedFields)"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_create_issue"), Description: "Create a new issue. Use jira_list_issue_types to find valid issue type names. Supports custom fields — use jira_list_fields to discover field IDs", - Parameters: map[string]string{"project_key": "Project key (e.g., PROJ)", "issue_type": "Issue type name (e.g., Bug, Task, Story)", "summary": "Issue summary/title", "description": "Issue description (plain text, converted to ADF)", "priority": "Priority name (e.g., High, Medium, Low)", "assignee_id": "Account ID of assignee (use jira_search_users to find)", "labels": "Comma-separated labels", "parent_key": "Parent issue key for subtasks (e.g., PROJ-100)", "custom_fields": `JSON object of custom field values keyed by field ID (e.g. {"customfield_10001": "value", "customfield_10002": {"id": "10100"}}). Use jira_list_fields to discover field IDs and types`}, - Required: []string{"project_key", "issue_type", "summary"}, - }, - { - Name: mcp.ToolName("jira_update_issue"), Description: "Update an existing issue's fields including custom fields — use jira_list_fields to discover custom field IDs", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "summary": "New summary", "description": "New description (plain text, converted to ADF)", "priority": "Priority name", "assignee_id": "Account ID of assignee (empty string to unassign)", "labels": "Comma-separated labels (replaces existing)", "custom_fields": `JSON object of custom field values keyed by field ID (e.g. {"customfield_10001": "value", "customfield_10002": {"id": "10100"}}). Use jira_list_fields to discover field IDs and types`}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_delete_issue"), Description: "Delete an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "delete_subtasks": "Also delete subtasks (true/false, default false)"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_transition_issue"), Description: "Transition an issue to a new status. Use jira_get_transitions first to find valid transition IDs", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "transition_id": "Transition ID (use jira_get_transitions to find valid IDs)"}, - Required: []string{"issue_key", "transition_id"}, - }, - { - Name: mcp.ToolName("jira_get_transitions"), Description: "List available transitions for an issue. Use before jira_transition_issue to find valid transition IDs", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_assign_issue"), Description: "Assign an issue to a user", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "account_id": "Account ID of assignee (use jira_search_users to find, or empty/-1 to unassign)"}, - Required: []string{"issue_key", "account_id"}, - }, - { - Name: mcp.ToolName("jira_list_comments"), Description: "List comments on an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "start_at": "Pagination offset", "max_results": "Max results per page"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_add_comment"), Description: "Add a comment to an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "body": "Comment body (plain text, converted to ADF)"}, - Required: []string{"issue_key", "body"}, - }, - { - Name: mcp.ToolName("jira_update_comment"), Description: "Update an existing comment", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "comment_id": "Comment ID", "body": "New comment body (plain text, converted to ADF)"}, - Required: []string{"issue_key", "comment_id", "body"}, - }, - { - Name: mcp.ToolName("jira_delete_comment"), Description: "Delete a comment from an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "comment_id": "Comment ID"}, - Required: []string{"issue_key", "comment_id"}, - }, - { - Name: mcp.ToolName("jira_list_issue_links"), Description: "List links on an issue (blocks, is blocked by, duplicates, etc.)", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_create_issue_link"), Description: "Create a link between two issues", - Parameters: map[string]string{"type_name": "Link type name (e.g., Blocks, Duplicate, Cloners)", "inward_issue": "Issue key for the inward side", "outward_issue": "Issue key for the outward side"}, - Required: []string{"type_name", "inward_issue", "outward_issue"}, - }, - { - Name: mcp.ToolName("jira_delete_issue_link"), Description: "Delete an issue link by link ID", - Parameters: map[string]string{"link_id": "Issue link ID"}, - Required: []string{"link_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Projects ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("jira_list_projects"), Description: "List all accessible projects", - Parameters: map[string]string{"start_at": "Pagination offset", "max_results": "Max results per page (default 50)", "query": "Filter projects by name"}, - }, - { - Name: mcp.ToolName("jira_get_project"), Description: "Get details of a specific project", - Parameters: map[string]string{"project_key": "Project key or ID"}, - Required: []string{"project_key"}, - }, - { - Name: mcp.ToolName("jira_list_project_components"), Description: "List components in a project", - Parameters: map[string]string{"project_key": "Project key or ID"}, - Required: []string{"project_key"}, - }, - { - Name: mcp.ToolName("jira_list_project_versions"), Description: "List versions (releases) in a project", - Parameters: map[string]string{"project_key": "Project key or ID"}, - Required: []string{"project_key"}, - }, - { - Name: mcp.ToolName("jira_list_project_statuses"), Description: "List valid statuses for a project's issue types", - Parameters: map[string]string{"project_key": "Project key or ID"}, - Required: []string{"project_key"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Boards & Sprints (Agile API) ──────────────────────────────── - { - Name: mcp.ToolName("jira_list_boards"), Description: "List all agile boards", - Parameters: map[string]string{"start_at": "Pagination offset", "max_results": "Max results per page (default 50)", "project_key": "Filter by project key", "type": "Board type: scrum, kanban, simple"}, - }, - { - Name: mcp.ToolName("jira_get_board"), Description: "Get details of an agile board", - Parameters: map[string]string{"board_id": "Board ID"}, - Required: []string{"board_id"}, - }, - { - Name: mcp.ToolName("jira_list_sprints"), Description: "List sprints for a board", - Parameters: map[string]string{"board_id": "Board ID", "start_at": "Pagination offset", "max_results": "Max results per page", "state": "Filter by state: active, future, closed"}, - Required: []string{"board_id"}, - }, - { - Name: mcp.ToolName("jira_get_sprint"), Description: "Get details of a sprint", - Parameters: map[string]string{"sprint_id": "Sprint ID"}, - Required: []string{"sprint_id"}, - }, - { - Name: mcp.ToolName("jira_create_sprint"), Description: "Create a new sprint", - Parameters: map[string]string{"board_id": "Board ID (origin board)", "name": "Sprint name", "start_date": "Start date (ISO 8601)", "end_date": "End date (ISO 8601)", "goal": "Sprint goal"}, - Required: []string{"board_id", "name"}, - }, - { - Name: mcp.ToolName("jira_update_sprint"), Description: "Update a sprint's details", - Parameters: map[string]string{"sprint_id": "Sprint ID", "name": "Sprint name", "state": "Sprint state: active, future, closed", "start_date": "Start date (ISO 8601)", "end_date": "End date (ISO 8601)", "goal": "Sprint goal"}, - Required: []string{"sprint_id"}, - }, - { - Name: mcp.ToolName("jira_get_sprint_issues"), Description: "List issues in a sprint", - Parameters: map[string]string{"sprint_id": "Sprint ID", "start_at": "Pagination offset", "max_results": "Max results per page", "jql": "Additional JQL filter"}, - Required: []string{"sprint_id"}, - }, - { - Name: mcp.ToolName("jira_move_issues_to_sprint"), Description: "Move issues to a sprint", - Parameters: map[string]string{"sprint_id": "Sprint ID", "issues": "Comma-separated issue keys (e.g., PROJ-1,PROJ-2)"}, - Required: []string{"sprint_id", "issues"}, - }, - { - Name: mcp.ToolName("jira_list_board_backlog"), Description: "List issues in the backlog of a board", - Parameters: map[string]string{"board_id": "Board ID", "start_at": "Pagination offset", "max_results": "Max results per page", "jql": "Additional JQL filter"}, - Required: []string{"board_id"}, - }, - { - Name: mcp.ToolName("jira_get_board_config"), Description: "Get configuration of an agile board (columns, estimation, ranking)", - Parameters: map[string]string{"board_id": "Board ID"}, - Required: []string{"board_id"}, - }, - - // ── Users ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("jira_get_myself"), Description: "Get the current authenticated user's details", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_search_users"), Description: "Search for users by name or email. Use to find account IDs for assignment", - Parameters: map[string]string{"query": "Search query (name or email)"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("jira_get_user"), Description: "Get details of a specific user by account ID", - Parameters: map[string]string{"account_id": "User account ID"}, - Required: []string{"account_id"}, - }, - - // ── Metadata ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("jira_list_issue_types"), Description: "List all issue types available in the Jira instance", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_list_priorities"), Description: "List all issue priorities", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_list_statuses"), Description: "List all issue statuses", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_list_labels"), Description: "List all labels used across the instance", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_list_fields"), Description: "List all fields (system and custom) available in the instance", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("jira_list_filters"), Description: "Search saved filters", - Parameters: map[string]string{"filter_name": "Filter by name (substring match)", "start_at": "Pagination offset", "max_results": "Max results per page"}, - }, - { - Name: mcp.ToolName("jira_get_filter"), Description: "Get details of a saved filter including its JQL", - Parameters: map[string]string{"filter_id": "Filter ID"}, - Required: []string{"filter_id"}, - }, - - // ── Worklogs & Info ────────────────────────────────────────────── - { - Name: mcp.ToolName("jira_list_worklogs"), Description: "List work logs for an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "start_at": "Pagination offset", "max_results": "Max results per page"}, - Required: []string{"issue_key"}, - }, - { - Name: mcp.ToolName("jira_add_worklog"), Description: "Add a work log entry to an issue", - Parameters: map[string]string{"issue_key": "Issue key (e.g., PROJ-123)", "time_spent": "Time spent (e.g., 2h, 30m, 1d)", "comment": "Work log comment (plain text)", "started": "When work started (ISO 8601, defaults to now)"}, - Required: []string{"issue_key", "time_spent"}, - }, - { - Name: mcp.ToolName("jira_get_server_info"), Description: "Get Jira instance server info (version, deployment type, URLs)", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/jira/tools.yaml b/integrations/jira/tools.yaml new file mode 100644 index 00000000..b22a69ff --- /dev/null +++ b/integrations/jira/tools.yaml @@ -0,0 +1,413 @@ +version: 1 +tools: + jira_search_issues: + description: "Search issues using JQL (Jira Query Language). Start here for most issue workflows. Returns paginated results; pass nextPageToken from response to fetch next page" + parameters: + jql: + description: "JQL query (e.g., 'project = PROJ AND status = Open')" + required: true + fields: + description: "Comma-separated fields to return (default: summary,status,assignee,priority,issuetype)" + next_page_token: + description: "Cursor for next page (from previous response's nextPageToken field)" + max_results: + description: "Max results per page (default 200, server may cap)" + + jira_get_issue: + description: "Get full details of a specific issue by key or ID" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123) or ID" + required: true + fields: + description: "Comma-separated fields to return (default: all)" + expand: + description: "Comma-separated expansions (e.g., changelog,renderedFields)" + + jira_create_issue: + description: "Create a new issue. Use jira_list_issue_types to find valid issue type names. Supports custom fields — use jira_list_fields to discover field IDs" + parameters: + project_key: + description: "Project key (e.g., PROJ)" + required: true + issue_type: + description: "Issue type name (e.g., Bug, Task, Story)" + required: true + summary: + description: "Issue summary/title" + required: true + description: + description: "Issue description (plain text, converted to ADF)" + priority: + description: "Priority name (e.g., High, Medium, Low)" + assignee_id: + description: "Account ID of assignee (use jira_search_users to find)" + labels: + description: "Comma-separated labels" + parent_key: + description: "Parent issue key for subtasks (e.g., PROJ-100)" + custom_fields: + description: 'JSON object of custom field values keyed by field ID (e.g. {"customfield_10001": "value", "customfield_10002": {"id": "10100"}}). Use jira_list_fields to discover field IDs and types' + + jira_update_issue: + description: "Update an existing issue's fields including custom fields — use jira_list_fields to discover custom field IDs" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + summary: + description: "New summary" + description: + description: "New description (plain text, converted to ADF)" + priority: + description: "Priority name" + assignee_id: + description: "Account ID of assignee (empty string to unassign)" + labels: + description: "Comma-separated labels (replaces existing)" + custom_fields: + description: 'JSON object of custom field values keyed by field ID (e.g. {"customfield_10001": "value", "customfield_10002": {"id": "10100"}}). Use jira_list_fields to discover field IDs and types' + + jira_delete_issue: + description: "Delete an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + delete_subtasks: + description: "Also delete subtasks (true/false, default false)" + + jira_transition_issue: + description: "Transition an issue to a new status. Use jira_get_transitions first to find valid transition IDs" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + transition_id: + description: "Transition ID (use jira_get_transitions to find valid IDs)" + required: true + + jira_get_transitions: + description: "List available transitions for an issue. Use before jira_transition_issue to find valid transition IDs" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + + jira_assign_issue: + description: "Assign an issue to a user" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + account_id: + description: "Account ID of assignee (use jira_search_users to find, or empty/-1 to unassign)" + required: true + + jira_list_comments: + description: "List comments on an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + + jira_add_comment: + description: "Add a comment to an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + body: + description: "Comment body (plain text, converted to ADF)" + required: true + + jira_update_comment: + description: "Update an existing comment" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + comment_id: + description: "Comment ID" + required: true + body: + description: "New comment body (plain text, converted to ADF)" + required: true + + jira_delete_comment: + description: "Delete a comment from an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + comment_id: + description: "Comment ID" + required: true + + jira_list_issue_links: + description: "List links on an issue (blocks, is blocked by, duplicates, etc.)" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + + jira_create_issue_link: + description: "Create a link between two issues" + parameters: + type_name: + description: "Link type name (e.g., Blocks, Duplicate, Cloners)" + required: true + inward_issue: + description: "Issue key for the inward side" + required: true + outward_issue: + description: "Issue key for the outward side" + required: true + + jira_delete_issue_link: + description: "Delete an issue link by link ID" + parameters: + link_id: + description: "Issue link ID" + required: true + + jira_list_projects: + description: "List all accessible projects" + parameters: + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page (default 50)" + query: + description: "Filter projects by name" + + jira_get_project: + description: "Get details of a specific project" + parameters: + project_key: + description: "Project key or ID" + required: true + + jira_list_project_components: + description: "List components in a project" + parameters: + project_key: + description: "Project key or ID" + required: true + + jira_list_project_versions: + description: "List versions (releases) in a project" + parameters: + project_key: + description: "Project key or ID" + required: true + + jira_list_project_statuses: + description: "List valid statuses for a project's issue types" + parameters: + project_key: + description: "Project key or ID" + required: true + + jira_list_boards: + description: "List all agile boards" + parameters: + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page (default 50)" + project_key: + description: "Filter by project key" + type: + description: "Board type: scrum, kanban, simple" + + jira_get_board: + description: "Get details of an agile board" + parameters: + board_id: + description: "Board ID" + required: true + + jira_list_sprints: + description: "List sprints for a board" + parameters: + board_id: + description: "Board ID" + required: true + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + state: + description: "Filter by state: active, future, closed" + + jira_get_sprint: + description: "Get details of a sprint" + parameters: + sprint_id: + description: "Sprint ID" + required: true + + jira_create_sprint: + description: "Create a new sprint" + parameters: + board_id: + description: "Board ID (origin board)" + required: true + name: + description: "Sprint name" + required: true + start_date: + description: "Start date (ISO 8601)" + end_date: + description: "End date (ISO 8601)" + goal: + description: "Sprint goal" + + jira_update_sprint: + description: "Update a sprint's details" + parameters: + sprint_id: + description: "Sprint ID" + required: true + name: + description: "Sprint name" + state: + description: "Sprint state: active, future, closed" + start_date: + description: "Start date (ISO 8601)" + end_date: + description: "End date (ISO 8601)" + goal: + description: "Sprint goal" + + jira_get_sprint_issues: + description: "List issues in a sprint" + parameters: + sprint_id: + description: "Sprint ID" + required: true + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + jql: + description: "Additional JQL filter" + + jira_move_issues_to_sprint: + description: "Move issues to a sprint" + parameters: + sprint_id: + description: "Sprint ID" + required: true + issues: + description: "Comma-separated issue keys (e.g., PROJ-1,PROJ-2)" + required: true + + jira_list_board_backlog: + description: "List issues in the backlog of a board" + parameters: + board_id: + description: "Board ID" + required: true + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + jql: + description: "Additional JQL filter" + + jira_get_board_config: + description: "Get configuration of an agile board (columns, estimation, ranking)" + parameters: + board_id: + description: "Board ID" + required: true + + jira_get_myself: + description: "Get the current authenticated user's details" + parameters: {} + + jira_search_users: + description: "Search for users by name or email. Use to find account IDs for assignment" + parameters: + query: + description: "Search query (name or email)" + required: true + + jira_get_user: + description: "Get details of a specific user by account ID" + parameters: + account_id: + description: "User account ID" + required: true + + jira_list_issue_types: + description: "List all issue types available in the Jira instance" + parameters: {} + + jira_list_priorities: + description: "List all issue priorities" + parameters: {} + + jira_list_statuses: + description: "List all issue statuses" + parameters: {} + + jira_list_labels: + description: "List all labels used across the instance" + parameters: {} + + jira_list_fields: + description: "List all fields (system and custom) available in the instance" + parameters: {} + + jira_list_filters: + description: "Search saved filters" + parameters: + filter_name: + description: "Filter by name (substring match)" + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + + jira_get_filter: + description: "Get details of a saved filter including its JQL" + parameters: + filter_id: + description: "Filter ID" + required: true + + jira_list_worklogs: + description: "List work logs for an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + start_at: + description: "Pagination offset" + max_results: + description: "Max results per page" + + jira_add_worklog: + description: "Add a work log entry to an issue" + parameters: + issue_key: + description: "Issue key (e.g., PROJ-123)" + required: true + time_spent: + description: "Time spent (e.g., 2h, 30m, 1d)" + required: true + comment: + description: "Work log comment (plain text)" + started: + description: "When work started (ISO 8601, defaults to now)" + + jira_get_server_info: + description: "Get Jira instance server info (version, deployment type, URLs)" + parameters: {} diff --git a/integrations/kubernetes/tools.go b/integrations/kubernetes/tools.go index 8afe9b2b..0f4e8e12 100644 --- a/integrations/kubernetes/tools.go +++ b/integrations/kubernetes/tools.go @@ -1,117 +1,12 @@ package kubernetes -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -const namespaceParamDesc = "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + mcp "github.com/daltoniam/switchboard" +) -var tools = []mcp.ToolDefinition{ - { - Name: mcp.ToolName("kubernetes_list_contexts"), - Description: "List Kubernetes kubeconfig contexts, clusters, users, and namespaces. Start here for local Kubernetes context discovery before choosing a cluster.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("kubernetes_list_namespaces"), - Description: "List Kubernetes namespaces in the cluster. Start here for Kubernetes cluster discovery and finding available environments.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("kubernetes_list_pods"), - Description: "List Kubernetes pods with phase, node, labels, restarts, and readiness. Start here for workload health, container debugging, and finding failing pods.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "all_namespaces": "Set true to list pods across all namespaces.", - "label_selector": "Kubernetes label selector, for example app=api,tier=backend.", - "field_selector": "Kubernetes field selector, for example status.phase=Running.", - "limit": "Maximum number of pods to return (default: 100, max: 500).", - }, - }, - { - Name: mcp.ToolName("kubernetes_get_pod"), - Description: "Get details for a specific Kubernetes pod, including containers, conditions, owner references, and recent status. Use after list_pods.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "pod": "Pod name.", - }, - Required: []string{"pod"}, - }, - { - Name: mcp.ToolName("kubernetes_read_pod_logs"), - Description: "Read logs from a Kubernetes pod container. Use after list_pods or get_pod for debugging crashes, errors, and workload failures.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "pod": "Pod name.", - "container": "Container name (optional for single-container pods).", - "tail": "Number of log lines from the end (default: 200, max: 2000).", - "previous": "Set true to read logs from the previous terminated container instance.", - "timestamps": "Set true to include log timestamps.", - }, - Required: []string{"pod"}, - }, - { - Name: mcp.ToolName("kubernetes_list_events"), - Description: "List Kubernetes events sorted by time. Use for debugging scheduling, image pulls, probes, restarts, and rollout failures.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "all_namespaces": "Set true to list events across all namespaces.", - "field_selector": "Kubernetes field selector, for example involvedObject.name=api.", - "limit": "Maximum number of events to return (default: 100, max: 500).", - }, - }, - { - Name: mcp.ToolName("kubernetes_list_deployments"), - Description: "List Kubernetes deployments with replicas, availability, labels, and images. Use for workload discovery and rollout health checks.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "all_namespaces": "Set true to list deployments across all namespaces.", - "label_selector": "Kubernetes label selector.", - "limit": "Maximum number of deployments to return (default: 100, max: 500).", - }, - }, - { - Name: mcp.ToolName("kubernetes_get_deployment"), - Description: "Get details for a specific Kubernetes deployment, including rollout conditions, replica counts, strategy, selectors, and images. Use after list_deployments.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "deployment": "Deployment name.", - }, - Required: []string{"deployment"}, - }, - { - Name: mcp.ToolName("kubernetes_rollout_status"), - Description: "Check Kubernetes deployment rollout status and explain whether a rollout is complete, progressing, or stuck. Use after list_deployments or get_deployment.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "deployment": "Deployment name.", - }, - Required: []string{"deployment"}, - }, - { - Name: mcp.ToolName("kubernetes_list_services"), - Description: "List Kubernetes services with type, cluster IPs, external IPs, ports, and selectors. Use for service discovery and network debugging.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "all_namespaces": "Set true to list services across all namespaces.", - "label_selector": "Kubernetes label selector.", - "limit": "Maximum number of services to return (default: 100, max: 500).", - }, - }, - { - Name: mcp.ToolName("kubernetes_list_ingresses"), - Description: "List Kubernetes ingresses with hosts, paths, classes, TLS, and load balancer addresses. Use for HTTP routing and exposure debugging.", - Parameters: map[string]string{ - "namespace": namespaceParamDesc, - "all_namespaces": "Set true to list ingresses across all namespaces.", - "label_selector": "Kubernetes label selector.", - "limit": "Maximum number of ingresses to return (default: 100, max: 500).", - }, - }, - { - Name: mcp.ToolName("kubernetes_list_nodes"), - Description: "List Kubernetes cluster nodes with readiness, versions, roles, taints, capacity, and allocatable resources. Use for cluster capacity and node health debugging.", - Parameters: map[string]string{ - "label_selector": "Kubernetes label selector.", - "limit": "Maximum number of nodes to return (default: 100, max: 500).", - }, - }, -} +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/kubernetes/tools.yaml b/integrations/kubernetes/tools.yaml new file mode 100644 index 00000000..d6d7198b --- /dev/null +++ b/integrations/kubernetes/tools.yaml @@ -0,0 +1,110 @@ +version: 1 +tools: + kubernetes_list_contexts: + description: "List Kubernetes kubeconfig contexts, clusters, users, and namespaces. Start here for local Kubernetes context discovery before choosing a cluster." + kubernetes_list_namespaces: + description: "List Kubernetes namespaces in the cluster. Start here for Kubernetes cluster discovery and finding available environments." + kubernetes_list_pods: + description: "List Kubernetes pods with phase, node, labels, restarts, and readiness. Start here for workload health, container debugging, and finding failing pods." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + all_namespaces: + description: "Set true to list pods across all namespaces." + label_selector: + description: "Kubernetes label selector, for example app=api,tier=backend." + field_selector: + description: "Kubernetes field selector, for example status.phase=Running." + limit: + description: "Maximum number of pods to return (default: 100, max: 500)." + kubernetes_get_pod: + description: "Get details for a specific Kubernetes pod, including containers, conditions, owner references, and recent status. Use after list_pods." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + pod: + description: "Pod name." + required: true + kubernetes_read_pod_logs: + description: "Read logs from a Kubernetes pod container. Use after list_pods or get_pod for debugging crashes, errors, and workload failures." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + pod: + description: "Pod name." + required: true + container: + description: "Container name (optional for single-container pods)." + tail: + description: "Number of log lines from the end (default: 200, max: 2000)." + previous: + description: "Set true to read logs from the previous terminated container instance." + timestamps: + description: "Set true to include log timestamps." + kubernetes_list_events: + description: "List Kubernetes events sorted by time. Use for debugging scheduling, image pulls, probes, restarts, and rollout failures." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + all_namespaces: + description: "Set true to list events across all namespaces." + field_selector: + description: "Kubernetes field selector, for example involvedObject.name=api." + limit: + description: "Maximum number of events to return (default: 100, max: 500)." + kubernetes_list_deployments: + description: "List Kubernetes deployments with replicas, availability, labels, and images. Use for workload discovery and rollout health checks." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + all_namespaces: + description: "Set true to list deployments across all namespaces." + label_selector: + description: "Kubernetes label selector." + limit: + description: "Maximum number of deployments to return (default: 100, max: 500)." + kubernetes_get_deployment: + description: "Get details for a specific Kubernetes deployment, including rollout conditions, replica counts, strategy, selectors, and images. Use after list_deployments." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + deployment: + description: "Deployment name." + required: true + kubernetes_rollout_status: + description: "Check Kubernetes deployment rollout status and explain whether a rollout is complete, progressing, or stuck. Use after list_deployments or get_deployment." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + deployment: + description: "Deployment name." + required: true + kubernetes_list_services: + description: "List Kubernetes services with type, cluster IPs, external IPs, ports, and selectors. Use for service discovery and network debugging." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + all_namespaces: + description: "Set true to list services across all namespaces." + label_selector: + description: "Kubernetes label selector." + limit: + description: "Maximum number of services to return (default: 100, max: 500)." + kubernetes_list_ingresses: + description: "List Kubernetes ingresses with hosts, paths, classes, TLS, and load balancer addresses. Use for HTTP routing and exposure debugging." + parameters: + namespace: + description: "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + all_namespaces: + description: "Set true to list ingresses across all namespaces." + label_selector: + description: "Kubernetes label selector." + limit: + description: "Maximum number of ingresses to return (default: 100, max: 500)." + kubernetes_list_nodes: + description: "List Kubernetes cluster nodes with readiness, versions, roles, taints, capacity, and allocatable resources. Use for cluster capacity and node health debugging." + parameters: + label_selector: + description: "Kubernetes label selector." + limit: + description: "Maximum number of nodes to return (default: 100, max: 500)." diff --git a/integrations/linear/tools.go b/integrations/linear/tools.go index d78b7f24..f3832667 100644 --- a/integrations/linear/tools.go +++ b/integrations/linear/tools.go @@ -1,334 +1,12 @@ package linear -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Issues ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_issues"), Description: "List Linear issues (tickets/bugs/tasks) with optional filters. Start here for filtered queries (by assignee, state, label, project). Use list_workflow_states to discover valid state names.", - Parameters: map[string]string{"team": "Filter by team name or key", "assignee": "Filter by assignee name or 'me'", "state": "Filter by state name (e.g., 'In Progress', 'Done')", "label": "Filter by label name", "priority": "Filter by priority (1=urgent, 2=high, 3=normal, 4=low)", "project": "Filter by project name", "cycle": "Filter by cycle name or number", "first": "Max results (default 50)", "after": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("linear_search_issues"), Description: "Full-text search Linear issues (tickets/bugs) by keyword. Start here to find issues by text. For filtering by assignee, state, or label, prefer list_issues instead.", - Parameters: map[string]string{"query": "Search query text", "first": "Max results (default 50)", "after": "Pagination cursor"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("linear_get_issue"), Description: "Get a specific issue with full detail. Accepts issue ID (UUID) or identifier (e.g., ENG-123).", - Parameters: map[string]string{"id": "Issue identifier (e.g., ENG-123) or UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_issue"), Description: "Create a new issue (ticket/bug/task). Requires team_id — use list_teams to find it. Use list_workflow_states to discover valid state names.", - Parameters: map[string]string{"title": "Issue title", "team": "Team name or key", "description": "Description (markdown)", "assignee": "Assignee name or email", "priority": "Priority (0=none, 1=urgent, 2=high, 3=normal, 4=low)", "state": "Workflow state name", "labels": "Comma-separated label names", "project": "Project name", "milestone": "Project milestone name or UUID", "cycle": "Cycle name or number", "estimate": "Story point estimate", "due_date": "Due date (YYYY-MM-DD)", "parent_id": "Parent issue ID for sub-issues"}, - Required: []string{"title", "team"}, - }, - { - Name: mcp.ToolName("linear_update_issue"), Description: "Update an existing issue. Accepts issue ID (UUID) or identifier (e.g., ENG-123). Use list_workflow_states to discover valid state names. Use list_teams to find team names for transfers.", - Parameters: map[string]string{"id": "Issue identifier (e.g., ENG-123) or UUID", "title": "New title", "description": "New description", "assignee": "Assignee name or email", "priority": "Priority (0-4)", "state": "Workflow state name", "team": "Team name or key (moves issue to this team)", "labels": "Comma-separated label names", "project": "Project name", "milestone": "Project milestone name or UUID", "estimate": "Story point estimate", "due_date": "Due date (YYYY-MM-DD)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_archive_issue"), Description: "Archive an issue", - Parameters: map[string]string{"id": "Issue identifier or UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_unarchive_issue"), Description: "Unarchive an issue", - Parameters: map[string]string{"id": "Issue identifier or UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_list_issue_comments"), Description: "List comments on an issue", - Parameters: map[string]string{"id": "Issue identifier or UUID", "first": "Max results (default 50)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_comment"), Description: "Create a comment on an issue", - Parameters: map[string]string{"issue_id": "Issue identifier or UUID", "body": "Comment body (markdown)"}, - Required: []string{"issue_id", "body"}, - }, - { - Name: mcp.ToolName("linear_update_comment"), Description: "Update a comment", - Parameters: map[string]string{"id": "Comment UUID", "body": "New comment body (markdown)"}, - Required: []string{"id", "body"}, - }, - { - Name: mcp.ToolName("linear_delete_comment"), Description: "Delete a comment", - Parameters: map[string]string{"id": "Comment UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_list_issue_relations"), Description: "List relations for an issue (blocks, blocked by, related, duplicate)", - Parameters: map[string]string{"id": "Issue identifier or UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_issue_relation"), Description: "Create a relation between two issues", - Parameters: map[string]string{"issue_id": "Source issue identifier or UUID", "related_issue_id": "Related issue identifier or UUID", "type": "Relation type: blocks, blocked_by, related, duplicate"}, - Required: []string{"issue_id", "related_issue_id", "type"}, - }, - { - Name: mcp.ToolName("linear_delete_issue_relation"), Description: "Delete an issue relation", - Parameters: map[string]string{"id": "Relation UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_list_issue_labels"), Description: "List labels on a specific issue", - Parameters: map[string]string{"id": "Issue identifier or UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_list_attachments"), Description: "List attachments on an issue", - Parameters: map[string]string{"issue_id": "Issue identifier or UUID", "first": "Max results (default 25)"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("linear_create_attachment"), Description: "Create an attachment (link) on an issue", - Parameters: map[string]string{"issue_id": "Issue identifier or UUID", "title": "Attachment title", "url": "Attachment URL", "subtitle": "Subtitle"}, - Required: []string{"issue_id", "url"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Projects ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_projects"), Description: "List projects with optional filters. Start here for project status queries.", - Parameters: map[string]string{"team": "Filter by team name or key", "state": "Filter by state: planned, started, paused, completed, canceled", "first": "Max results (default 50)", "after": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("linear_search_projects"), Description: "Find Linear projects by name or keyword. Returns project IDs needed by get_project and list_project_updates. Start here when you know the project name.", - Parameters: map[string]string{"query": "Project name or keyword", "first": "Max results (default 10)"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("linear_get_project"), Description: "Get a specific project with full detail including progress, members, recent status updates, and milestones. Accepts project UUID or slug. Use search_projects to find the ID first.", - Parameters: map[string]string{"id": "Project UUID or slug"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_project"), Description: "Create a project", - Parameters: map[string]string{"name": "Project name", "team": "Team name or key", "description": "Description (markdown)", "state": "State: planned, started, paused, completed, canceled", "lead": "Project lead name or email", "target_date": "Target date (YYYY-MM-DD)", "start_date": "Start date (YYYY-MM-DD)"}, - Required: []string{"name", "team"}, - }, - { - Name: mcp.ToolName("linear_update_project"), Description: "Update a project", - Parameters: map[string]string{"id": "Project UUID", "name": "New name", "description": "New description", "state": "State: planned, started, paused, completed, canceled", "lead": "Project lead name or email", "target_date": "Target date", "start_date": "Start date"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_archive_project"), Description: "Archive a project", - Parameters: map[string]string{"id": "Project UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_list_project_updates"), Description: "List status updates (health reports) for a project. Requires project UUID — use search_projects to find it. For a quick summary, get_project already includes the 5 most recent updates.", - Parameters: map[string]string{"project_id": "Project UUID", "first": "Max results (default 10)"}, - Required: []string{"project_id"}, - }, - { - Name: mcp.ToolName("linear_create_project_update"), Description: "Create a project status update", - Parameters: map[string]string{"project_id": "Project UUID", "body": "Update body (markdown)", "health": "Health: onTrack, atRisk, offTrack"}, - Required: []string{"project_id", "body"}, - }, - { - Name: mcp.ToolName("linear_list_project_milestones"), Description: "List milestones for a project", - Parameters: map[string]string{"project_id": "Project UUID", "first": "Max results (default 50)"}, - Required: []string{"project_id"}, - }, - { - Name: mcp.ToolName("linear_create_project_milestone"), Description: "Create a project milestone", - Parameters: map[string]string{"project_id": "Project UUID", "name": "Milestone name", "description": "Description", "target_date": "Target date (YYYY-MM-DD)", "sort_order": "Sort order (number)"}, - Required: []string{"project_id", "name"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Cycles ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_cycles"), Description: "List cycles (sprints) with optional filters. Use to find the current sprint, then get_cycle for its issues.", - Parameters: map[string]string{"team": "Filter by team name or key", "first": "Max results (default 50)", "after": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("linear_get_cycle"), Description: "Get a specific cycle with its issues. Use after list_cycles to drill into a sprint.", - Parameters: map[string]string{"id": "Cycle UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_cycle"), Description: "Create a cycle", - Parameters: map[string]string{"team": "Team name or key", "name": "Cycle name", "description": "Description", "starts_at": "Start date (ISO 8601)", "ends_at": "End date (ISO 8601)"}, - Required: []string{"team", "starts_at", "ends_at"}, - }, - { - Name: mcp.ToolName("linear_update_cycle"), Description: "Update a cycle", - Parameters: map[string]string{"id": "Cycle UUID", "name": "New name", "description": "New description", "starts_at": "Start date", "ends_at": "End date"}, - Required: []string{"id"}, - }, - - // ── Teams ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_teams"), Description: "List all teams in the workspace. Use to discover team IDs needed by create_issue and list_workflow_states.", - Parameters: map[string]string{"first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_get_team"), Description: "Get a specific team with members and settings. Accepts team UUID, name, or key.", - Parameters: map[string]string{"id": "Team UUID, name, or key"}, - Required: []string{"id"}, - }, - - // ── Users ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_viewer"), Description: "Get the currently authenticated user. Use to find your user ID for filtering assigned issues via list_issues.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("linear_list_users"), Description: "List users in the workspace. Use to find user IDs for assignee filtering.", - Parameters: map[string]string{"first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_get_user"), Description: "Get a specific user with assigned issues count. Accepts user UUID, display name, or email.", - Parameters: map[string]string{"id": "User UUID, display name, or email"}, - Required: []string{"id"}, - }, - - // ── Labels ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_labels"), Description: "List issue labels in the workspace. Use to discover valid label names for list_issues filtering or create_issue.", - Parameters: map[string]string{"team": "Filter by team name or key", "first": "Max results (default 100)"}, - }, - { - Name: mcp.ToolName("linear_create_label"), Description: "Create an issue label", - Parameters: map[string]string{"name": "Label name", "color": "Hex color (e.g., #ff0000)", "team": "Team name or key (omit for workspace label)", "description": "Description"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("linear_update_label"), Description: "Update an issue label", - Parameters: map[string]string{"id": "Label UUID", "name": "New name", "color": "New hex color", "description": "New description"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_delete_label"), Description: "Delete an issue label", - Parameters: map[string]string{"id": "Label UUID"}, - Required: []string{"id"}, - }, - - // ── Workflow States ─────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_workflow_states"), Description: "List workflow states for a team. Use before list_issues, create_issue, or update_issue to discover valid state names.", - Parameters: map[string]string{"team": "Team name or key", "first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_create_workflow_state"), Description: "Create a workflow state", - Parameters: map[string]string{"team": "Team name or key", "name": "State name", "type": "Type: triage, backlog, unstarted, started, completed, canceled", "color": "Hex color", "description": "Description", "position": "Sort position (number)"}, - Required: []string{"team", "name", "type", "color"}, - }, - - // ── Documents ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_documents"), Description: "List documents in the workspace. Filter by project to find project-specific docs.", - Parameters: map[string]string{"project": "Filter by project name", "first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_search_documents"), Description: "Full-text search documents by keyword. For browsing by project, use list_documents with project filter.", - Parameters: map[string]string{"query": "Search query", "first": "Max results (default 25)"}, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("linear_get_document"), Description: "Get a specific document by ID", - Parameters: map[string]string{"id": "Document UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_document"), Description: "Create a document", - Parameters: map[string]string{"title": "Document title", "content": "Content (markdown)", "project": "Associated project name or UUID", "icon": "Icon emoji"}, - Required: []string{"title"}, - }, - { - Name: mcp.ToolName("linear_update_document"), Description: "Update a document", - Parameters: map[string]string{"id": "Document UUID", "title": "New title", "content": "New content (markdown)", "icon": "New icon emoji"}, - Required: []string{"id"}, - }, - - // ── Initiatives ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_initiatives"), Description: "List initiatives", - Parameters: map[string]string{"first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_get_initiative"), Description: "Get a specific initiative by ID", - Parameters: map[string]string{"id": "Initiative UUID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("linear_create_initiative"), Description: "Create an initiative", - Parameters: map[string]string{"name": "Initiative name", "description": "Description (markdown)", "target_date": "Target date (YYYY-MM-DD)", "status": "Status: Planned, Active, Completed"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("linear_update_initiative"), Description: "Update an initiative", - Parameters: map[string]string{"id": "Initiative UUID", "name": "New name", "description": "New description", "target_date": "Target date", "status": "Status: Planned, Active, Completed"}, - Required: []string{"id"}, - }, - - // ── Favorites ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_favorites"), Description: "List favorites for the authenticated user", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("linear_create_favorite"), Description: "Add an item to favorites", - Parameters: map[string]string{"issue_id": "Issue UUID", "project_id": "Project UUID", "cycle_id": "Cycle UUID", "custom_view_id": "Custom view UUID"}, - }, - { - Name: mcp.ToolName("linear_delete_favorite"), Description: "Remove a favorite", - Parameters: map[string]string{"id": "Favorite UUID"}, - Required: []string{"id"}, - }, - - // ── Webhooks ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_webhooks"), Description: "List webhooks in the workspace", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("linear_create_webhook"), Description: "Create a webhook", - Parameters: map[string]string{"url": "Webhook URL", "team": "Team name or key (omit for all teams)", "label": "Label to filter events", "resource_types": "Comma-separated resource types: Issue, Comment, Project, Cycle, IssueLabel, etc.", "all_public_teams": "Subscribe to all public teams (true/false)"}, - Required: []string{"url"}, - }, - { - Name: mcp.ToolName("linear_delete_webhook"), Description: "Delete a webhook", - Parameters: map[string]string{"id": "Webhook UUID"}, - Required: []string{"id"}, - }, - - // ── Notifications ───────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_notifications"), Description: "List notifications for the authenticated user", - Parameters: map[string]string{"first": "Max results (default 50)"}, - }, - - // ── Templates ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_templates"), Description: "List issue templates", - Parameters: map[string]string{}, - }, - - // ── Organization ────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_get_organization"), Description: "Get the current organization details", - Parameters: map[string]string{}, - }, - - // ── Custom Views ────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_list_custom_views"), Description: "List custom views (saved filters)", - Parameters: map[string]string{"first": "Max results (default 50)"}, - }, - { - Name: mcp.ToolName("linear_create_custom_view"), Description: "Create a custom view (saved filter)", - Parameters: map[string]string{"name": "View name", "description": "Description", "team": "Team name or key", "filter_state": "Filter by state names (comma-separated)", "filter_assignee": "Filter by assignee (name or 'me')", "filter_label": "Filter by label name", "filter_priority": "Filter by priority (1-4)"}, - Required: []string{"name"}, - }, - - // ── Rate Limit ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("linear_rate_limit"), Description: "Get current API rate limit status", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/linear/tools.yaml b/integrations/linear/tools.yaml new file mode 100644 index 00000000..a8740062 --- /dev/null +++ b/integrations/linear/tools.yaml @@ -0,0 +1,598 @@ +version: 1 +tools: + linear_list_issues: + description: "List Linear issues (tickets/bugs/tasks) with optional filters. Start here for filtered queries (by assignee, state, label, project). Use list_workflow_states to discover valid state names." + parameters: + team: + description: "Filter by team name or key" + assignee: + description: "Filter by assignee name or 'me'" + state: + description: "Filter by state name (e.g., 'In Progress', 'Done')" + label: + description: "Filter by label name" + priority: + description: "Filter by priority (1=urgent, 2=high, 3=normal, 4=low)" + project: + description: "Filter by project name" + cycle: + description: "Filter by cycle name or number" + first: + description: "Max results (default 50)" + after: + description: "Pagination cursor" + linear_search_issues: + description: "Full-text search Linear issues (tickets/bugs) by keyword. Start here to find issues by text. For filtering by assignee, state, or label, prefer list_issues instead." + parameters: + query: + description: "Search query text" + required: true + first: + description: "Max results (default 50)" + after: + description: "Pagination cursor" + linear_get_issue: + description: "Get a specific issue with full detail. Accepts issue ID (UUID) or identifier (e.g., ENG-123)." + parameters: + id: + description: "Issue identifier (e.g., ENG-123) or UUID" + required: true + linear_create_issue: + description: "Create a new issue (ticket/bug/task). Requires team_id — use list_teams to find it. Use list_workflow_states to discover valid state names." + parameters: + title: + description: "Issue title" + required: true + team: + description: "Team name or key" + required: true + description: + description: "Description (markdown)" + assignee: + description: "Assignee name or email" + priority: + description: "Priority (0=none, 1=urgent, 2=high, 3=normal, 4=low)" + state: + description: "Workflow state name" + labels: + description: "Comma-separated label names" + project: + description: "Project name" + milestone: + description: "Project milestone name or UUID" + cycle: + description: "Cycle name or number" + estimate: + description: "Story point estimate" + due_date: + description: "Due date (YYYY-MM-DD)" + parent_id: + description: "Parent issue ID for sub-issues" + linear_update_issue: + description: "Update an existing issue. Accepts issue ID (UUID) or identifier (e.g., ENG-123). Use list_workflow_states to discover valid state names. Use list_teams to find team names for transfers." + parameters: + id: + description: "Issue identifier (e.g., ENG-123) or UUID" + required: true + title: + description: "New title" + description: + description: "New description" + assignee: + description: "Assignee name or email" + priority: + description: "Priority (0-4)" + state: + description: "Workflow state name" + team: + description: "Team name or key (moves issue to this team)" + labels: + description: "Comma-separated label names" + project: + description: "Project name" + milestone: + description: "Project milestone name or UUID" + estimate: + description: "Story point estimate" + due_date: + description: "Due date (YYYY-MM-DD)" + linear_archive_issue: + description: "Archive an issue" + parameters: + id: + description: "Issue identifier or UUID" + required: true + linear_unarchive_issue: + description: "Unarchive an issue" + parameters: + id: + description: "Issue identifier or UUID" + required: true + linear_list_issue_comments: + description: "List comments on an issue" + parameters: + id: + description: "Issue identifier or UUID" + required: true + first: + description: "Max results (default 50)" + linear_create_comment: + description: "Create a comment on an issue" + parameters: + issue_id: + description: "Issue identifier or UUID" + required: true + body: + description: "Comment body (markdown)" + required: true + linear_update_comment: + description: "Update a comment" + parameters: + id: + description: "Comment UUID" + required: true + body: + description: "New comment body (markdown)" + required: true + linear_delete_comment: + description: "Delete a comment" + parameters: + id: + description: "Comment UUID" + required: true + linear_list_issue_relations: + description: "List relations for an issue (blocks, blocked by, related, duplicate)" + parameters: + id: + description: "Issue identifier or UUID" + required: true + linear_create_issue_relation: + description: "Create a relation between two issues" + parameters: + issue_id: + description: "Source issue identifier or UUID" + required: true + related_issue_id: + description: "Related issue identifier or UUID" + required: true + type: + description: "Relation type: blocks, blocked_by, related, duplicate" + required: true + linear_delete_issue_relation: + description: "Delete an issue relation" + parameters: + id: + description: "Relation UUID" + required: true + linear_list_issue_labels: + description: "List labels on a specific issue" + parameters: + id: + description: "Issue identifier or UUID" + required: true + linear_list_attachments: + description: "List attachments on an issue" + parameters: + issue_id: + description: "Issue identifier or UUID" + required: true + first: + description: "Max results (default 25)" + linear_create_attachment: + description: "Create an attachment (link) on an issue" + parameters: + issue_id: + description: "Issue identifier or UUID" + required: true + title: + description: "Attachment title" + url: + description: "Attachment URL" + required: true + subtitle: + description: "Subtitle" + linear_list_projects: + description: "List projects with optional filters. Start here for project status queries." + parameters: + team: + description: "Filter by team name or key" + state: + description: "Filter by state: planned, started, paused, completed, canceled" + first: + description: "Max results (default 50)" + after: + description: "Pagination cursor" + linear_search_projects: + description: "Find Linear projects by name or keyword. Returns project IDs needed by get_project and list_project_updates. Start here when you know the project name." + parameters: + query: + description: "Project name or keyword" + required: true + first: + description: "Max results (default 10)" + linear_get_project: + description: "Get a specific project with full detail including progress, members, recent status updates, and milestones. Accepts project UUID or slug. Use search_projects to find the ID first." + parameters: + id: + description: "Project UUID or slug" + required: true + linear_create_project: + description: "Create a project" + parameters: + name: + description: "Project name" + required: true + team: + description: "Team name or key" + required: true + description: + description: "Description (markdown)" + state: + description: "State: planned, started, paused, completed, canceled" + lead: + description: "Project lead name or email" + target_date: + description: "Target date (YYYY-MM-DD)" + start_date: + description: "Start date (YYYY-MM-DD)" + linear_update_project: + description: "Update a project" + parameters: + id: + description: "Project UUID" + required: true + name: + description: "New name" + description: + description: "New description" + state: + description: "State: planned, started, paused, completed, canceled" + lead: + description: "Project lead name or email" + target_date: + description: "Target date" + start_date: + description: "Start date" + linear_archive_project: + description: "Archive a project" + parameters: + id: + description: "Project UUID" + required: true + linear_list_project_updates: + description: "List status updates (health reports) for a project. Requires project UUID — use search_projects to find it. For a quick summary, get_project already includes the 5 most recent updates." + parameters: + project_id: + description: "Project UUID" + required: true + first: + description: "Max results (default 10)" + linear_create_project_update: + description: "Create a project status update" + parameters: + project_id: + description: "Project UUID" + required: true + body: + description: "Update body (markdown)" + required: true + health: + description: "Health: onTrack, atRisk, offTrack" + linear_list_project_milestones: + description: "List milestones for a project" + parameters: + project_id: + description: "Project UUID" + required: true + first: + description: "Max results (default 50)" + linear_create_project_milestone: + description: "Create a project milestone" + parameters: + project_id: + description: "Project UUID" + required: true + name: + description: "Milestone name" + required: true + description: + description: "Description" + target_date: + description: "Target date (YYYY-MM-DD)" + sort_order: + description: "Sort order (number)" + linear_list_cycles: + description: "List cycles (sprints) with optional filters. Use to find the current sprint, then get_cycle for its issues." + parameters: + team: + description: "Filter by team name or key" + first: + description: "Max results (default 50)" + after: + description: "Pagination cursor" + linear_get_cycle: + description: "Get a specific cycle with its issues. Use after list_cycles to drill into a sprint." + parameters: + id: + description: "Cycle UUID" + required: true + linear_create_cycle: + description: "Create a cycle" + parameters: + team: + description: "Team name or key" + required: true + name: + description: "Cycle name" + description: + description: "Description" + starts_at: + description: "Start date (ISO 8601)" + required: true + ends_at: + description: "End date (ISO 8601)" + required: true + linear_update_cycle: + description: "Update a cycle" + parameters: + id: + description: "Cycle UUID" + required: true + name: + description: "New name" + description: + description: "New description" + starts_at: + description: "Start date" + ends_at: + description: "End date" + linear_list_teams: + description: "List all teams in the workspace. Use to discover team IDs needed by create_issue and list_workflow_states." + parameters: + first: + description: "Max results (default 50)" + linear_get_team: + description: "Get a specific team with members and settings. Accepts team UUID, name, or key." + parameters: + id: + description: "Team UUID, name, or key" + required: true + linear_viewer: + description: "Get the currently authenticated user. Use to find your user ID for filtering assigned issues via list_issues." + parameters: {} + linear_list_users: + description: "List users in the workspace. Use to find user IDs for assignee filtering." + parameters: + first: + description: "Max results (default 50)" + linear_get_user: + description: "Get a specific user with assigned issues count. Accepts user UUID, display name, or email." + parameters: + id: + description: "User UUID, display name, or email" + required: true + linear_list_labels: + description: "List issue labels in the workspace. Use to discover valid label names for list_issues filtering or create_issue." + parameters: + team: + description: "Filter by team name or key" + first: + description: "Max results (default 100)" + linear_create_label: + description: "Create an issue label" + parameters: + name: + description: "Label name" + required: true + color: + description: "Hex color (e.g., #ff0000)" + team: + description: "Team name or key (omit for workspace label)" + description: + description: "Description" + linear_update_label: + description: "Update an issue label" + parameters: + id: + description: "Label UUID" + required: true + name: + description: "New name" + color: + description: "New hex color" + description: + description: "New description" + linear_delete_label: + description: "Delete an issue label" + parameters: + id: + description: "Label UUID" + required: true + linear_list_workflow_states: + description: "List workflow states for a team. Use before list_issues, create_issue, or update_issue to discover valid state names." + parameters: + team: + description: "Team name or key" + first: + description: "Max results (default 50)" + linear_create_workflow_state: + description: "Create a workflow state" + parameters: + team: + description: "Team name or key" + required: true + name: + description: "State name" + required: true + type: + description: "Type: triage, backlog, unstarted, started, completed, canceled" + required: true + color: + description: "Hex color" + required: true + description: + description: "Description" + position: + description: "Sort position (number)" + linear_list_documents: + description: "List documents in the workspace. Filter by project to find project-specific docs." + parameters: + project: + description: "Filter by project name" + first: + description: "Max results (default 50)" + linear_search_documents: + description: "Full-text search documents by keyword. For browsing by project, use list_documents with project filter." + parameters: + query: + description: "Search query" + required: true + first: + description: "Max results (default 25)" + linear_get_document: + description: "Get a specific document by ID" + parameters: + id: + description: "Document UUID" + required: true + linear_create_document: + description: "Create a document" + parameters: + title: + description: "Document title" + required: true + content: + description: "Content (markdown)" + project: + description: "Associated project name or UUID" + icon: + description: "Icon emoji" + linear_update_document: + description: "Update a document" + parameters: + id: + description: "Document UUID" + required: true + title: + description: "New title" + content: + description: "New content (markdown)" + icon: + description: "New icon emoji" + linear_list_initiatives: + description: "List initiatives" + parameters: + first: + description: "Max results (default 50)" + linear_get_initiative: + description: "Get a specific initiative by ID" + parameters: + id: + description: "Initiative UUID" + required: true + linear_create_initiative: + description: "Create an initiative" + parameters: + name: + description: "Initiative name" + required: true + description: + description: "Description (markdown)" + target_date: + description: "Target date (YYYY-MM-DD)" + status: + description: "Status: Planned, Active, Completed" + linear_update_initiative: + description: "Update an initiative" + parameters: + id: + description: "Initiative UUID" + required: true + name: + description: "New name" + description: + description: "New description" + target_date: + description: "Target date" + status: + description: "Status: Planned, Active, Completed" + linear_list_favorites: + description: "List favorites for the authenticated user" + parameters: {} + linear_create_favorite: + description: "Add an item to favorites" + parameters: + issue_id: + description: "Issue UUID" + project_id: + description: "Project UUID" + cycle_id: + description: "Cycle UUID" + custom_view_id: + description: "Custom view UUID" + linear_delete_favorite: + description: "Remove a favorite" + parameters: + id: + description: "Favorite UUID" + required: true + linear_list_webhooks: + description: "List webhooks in the workspace" + parameters: {} + linear_create_webhook: + description: "Create a webhook" + parameters: + url: + description: "Webhook URL" + required: true + team: + description: "Team name or key (omit for all teams)" + label: + description: "Label to filter events" + resource_types: + description: "Comma-separated resource types: Issue, Comment, Project, Cycle, IssueLabel, etc." + all_public_teams: + description: "Subscribe to all public teams (true/false)" + linear_delete_webhook: + description: "Delete a webhook" + parameters: + id: + description: "Webhook UUID" + required: true + linear_list_notifications: + description: "List notifications for the authenticated user" + parameters: + first: + description: "Max results (default 50)" + linear_list_templates: + description: "List issue templates" + parameters: {} + linear_get_organization: + description: "Get the current organization details" + parameters: {} + linear_list_custom_views: + description: "List custom views (saved filters)" + parameters: + first: + description: "Max results (default 50)" + linear_create_custom_view: + description: "Create a custom view (saved filter)" + parameters: + name: + description: "View name" + required: true + description: + description: "Description" + team: + description: "Team name or key" + filter_state: + description: "Filter by state names (comma-separated)" + filter_assignee: + description: "Filter by assignee (name or 'me')" + filter_label: + description: "Filter by label name" + filter_priority: + description: "Filter by priority (1-4)" + linear_rate_limit: + description: "Get current API rate limit status" + parameters: {} diff --git a/integrations/metabase/tools.go b/integrations/metabase/tools.go index 7a89bc0d..b974fc14 100644 --- a/integrations/metabase/tools.go +++ b/integrations/metabase/tools.go @@ -1,206 +1,15 @@ package metabase -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // --- Databases --- - { - Name: mcp.ToolName("metabase_list_databases"), - Description: "List all databases configured in Metabase", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("metabase_get_database"), - Description: "Get details of a specific database including its tables", - Parameters: map[string]string{"database_id": "Database ID"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("metabase_list_tables"), - Description: "List all tables in a specific database with metadata", - Parameters: map[string]string{"database_id": "Database ID"}, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("metabase_get_table"), - Description: "Get detailed metadata for a specific table", - Parameters: map[string]string{"table_id": "Table ID"}, - Required: []string{"table_id"}, - }, - { - Name: mcp.ToolName("metabase_get_table_fields"), - Description: "Get all fields/columns for a specific table with types and metadata", - Parameters: map[string]string{"table_id": "Table ID"}, - Required: []string{"table_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // --- Queries --- - { - Name: mcp.ToolName("metabase_execute_query"), - Description: "Execute a native SQL analytics query against a Metabase-connected database and return results as JSON. Use for ad-hoc analytics, BI reporting, and data exploration.", - Parameters: map[string]string{ - "database_id": "Database ID to query", - "query": "SQL query string", - }, - Required: []string{"database_id", "query"}, - }, - { - Name: mcp.ToolName("metabase_execute_card"), - Description: "Execute a saved question/card and return its results", - Parameters: map[string]string{ - "card_id": "Card/question ID", - "parameters": "Optional JSON array of parameter objects [{type, target, value}]", - }, - Required: []string{"card_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Cards (Saved Questions) --- - { - Name: mcp.ToolName("metabase_list_cards"), - Description: "List all saved questions/cards. Optionally filter by type.", - Parameters: map[string]string{ - "filter": "Filter: all (default), mine, bookmarked, archived", - }, - }, - { - Name: mcp.ToolName("metabase_get_card"), - Description: "Get details of a specific saved question/card", - Parameters: map[string]string{"card_id": "Card ID"}, - Required: []string{"card_id"}, - }, - { - Name: mcp.ToolName("metabase_create_card"), - Description: "Create a new saved question/card with a native SQL query", - Parameters: map[string]string{ - "name": "Name of the question", - "database_id": "Database ID", - "query": "SQL query string", - "description": "Optional description", - "collection_id": "Optional collection ID to save into", - "display": "Visualization type: table, bar, line, pie, scalar, etc. (default: table)", - }, - Required: []string{"name", "database_id", "query"}, - }, - { - Name: mcp.ToolName("metabase_update_card"), - Description: "Update a saved question/card (name, description, query, visualization)", - Parameters: map[string]string{ - "card_id": "Card ID", - "name": "New name", - "description": "New description", - "query": "New SQL query", - "database_id": "Database ID (required if changing query)", - "display": "Visualization type: table, bar, line, pie, scalar, etc.", - "archived": "Set to true to archive the card", - }, - Required: []string{"card_id"}, - }, - { - Name: mcp.ToolName("metabase_delete_card"), - Description: "Delete (archive) a saved question/card", - Parameters: map[string]string{"card_id": "Card ID"}, - Required: []string{"card_id"}, - }, - - // --- Dashboards --- - { - Name: mcp.ToolName("metabase_list_dashboards"), - Description: "List all Metabase analytics dashboards for reporting and data visualization", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("metabase_get_dashboard"), - Description: "Get details of a dashboard including its cards and layout", - Parameters: map[string]string{"dashboard_id": "Dashboard ID"}, - Required: []string{"dashboard_id"}, - }, - { - Name: mcp.ToolName("metabase_create_dashboard"), - Description: "Create a new dashboard", - Parameters: map[string]string{ - "name": "Dashboard name", - "description": "Optional description", - "collection_id": "Optional collection ID", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("metabase_update_dashboard"), - Description: "Update a dashboard's name, description, or other properties", - Parameters: map[string]string{ - "dashboard_id": "Dashboard ID", - "name": "New name", - "description": "New description", - "archived": "Set to true to archive the dashboard", - }, - Required: []string{"dashboard_id"}, - }, - { - Name: mcp.ToolName("metabase_delete_dashboard"), - Description: "Delete (archive) a dashboard", - Parameters: map[string]string{"dashboard_id": "Dashboard ID"}, - Required: []string{"dashboard_id"}, - }, - { - Name: mcp.ToolName("metabase_add_card_to_dashboard"), - Description: "Add a saved question/card to a dashboard", - Parameters: map[string]string{ - "dashboard_id": "Dashboard ID", - "card_id": "Card ID to add", - "size_x": "Width in grid units (default: 6)", - "size_y": "Height in grid units (default: 4)", - "row": "Row position (default: 0)", - "col": "Column position (default: 0)", - }, - Required: []string{"dashboard_id", "card_id"}, - }, - - // --- Collections --- - { - Name: mcp.ToolName("metabase_list_collections"), - Description: "List all collections (folders for organizing questions and dashboards)", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("metabase_get_collection"), - Description: "Get details and items in a specific collection", - Parameters: map[string]string{"collection_id": "Collection ID (use 'root' for the root collection)"}, - Required: []string{"collection_id"}, - }, - { - Name: mcp.ToolName("metabase_create_collection"), - Description: "Create a new collection for organizing questions and dashboards", - Parameters: map[string]string{ - "name": "Collection name", - "description": "Optional description", - "parent_id": "Optional parent collection ID for nesting", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("metabase_update_collection"), - Description: "Update a collection's name, description, or parent", - Parameters: map[string]string{ - "collection_id": "Collection ID", - "name": "New name", - "description": "New description", - "parent_id": "New parent collection ID", - "archived": "Set to true to archive", - }, - Required: []string{"collection_id"}, - }, - - // --- Search --- - { - Name: mcp.ToolName("metabase_search"), - Description: "Search across all Metabase content (questions, dashboards, collections, tables, databases). Start here for BI and reporting workflows.", - Parameters: map[string]string{ - "query": "Search query string", - "models": "Comma-separated types to search: card, dashboard, collection, table, database", - }, - Required: []string{"query"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ // Databases diff --git a/integrations/metabase/tools.yaml b/integrations/metabase/tools.yaml new file mode 100644 index 00000000..973d4839 --- /dev/null +++ b/integrations/metabase/tools.yaml @@ -0,0 +1,194 @@ +version: 1 +tools: + metabase_list_databases: + description: "List all databases configured in Metabase" + parameters: {} + metabase_get_database: + description: "Get details of a specific database including its tables" + parameters: + database_id: + description: "Database ID" + required: true + metabase_list_tables: + description: "List all tables in a specific database with metadata" + parameters: + database_id: + description: "Database ID" + required: true + metabase_get_table: + description: "Get detailed metadata for a specific table" + parameters: + table_id: + description: "Table ID" + required: true + metabase_get_table_fields: + description: "Get all fields/columns for a specific table with types and metadata" + parameters: + table_id: + description: "Table ID" + required: true + metabase_execute_query: + description: "Execute a native SQL analytics query against a Metabase-connected database and return results as JSON. Use for ad-hoc analytics, BI reporting, and data exploration." + parameters: + database_id: + description: "Database ID to query" + required: true + query: + description: "SQL query string" + required: true + metabase_execute_card: + description: "Execute a saved question/card and return its results" + parameters: + card_id: + description: "Card/question ID" + required: true + parameters: + description: "Optional JSON array of parameter objects [{type, target, value}]" + metabase_list_cards: + description: "List all saved questions/cards. Optionally filter by type." + parameters: + filter: + description: "Filter: all (default), mine, bookmarked, archived" + metabase_get_card: + description: "Get details of a specific saved question/card" + parameters: + card_id: + description: "Card ID" + required: true + metabase_create_card: + description: "Create a new saved question/card with a native SQL query" + parameters: + name: + description: "Name of the question" + required: true + database_id: + description: "Database ID" + required: true + query: + description: "SQL query string" + required: true + description: + description: "Optional description" + collection_id: + description: "Optional collection ID to save into" + display: + description: "Visualization type: table, bar, line, pie, scalar, etc. (default: table)" + metabase_update_card: + description: "Update a saved question/card (name, description, query, visualization)" + parameters: + card_id: + description: "Card ID" + required: true + name: + description: "New name" + description: + description: "New description" + query: + description: "New SQL query" + database_id: + description: "Database ID (required if changing query)" + display: + description: "Visualization type: table, bar, line, pie, scalar, etc." + archived: + description: "Set to true to archive the card" + metabase_delete_card: + description: "Delete (archive) a saved question/card" + parameters: + card_id: + description: "Card ID" + required: true + metabase_list_dashboards: + description: "List all Metabase analytics dashboards for reporting and data visualization" + parameters: {} + metabase_get_dashboard: + description: "Get details of a dashboard including its cards and layout" + parameters: + dashboard_id: + description: "Dashboard ID" + required: true + metabase_create_dashboard: + description: "Create a new dashboard" + parameters: + name: + description: "Dashboard name" + required: true + description: + description: "Optional description" + collection_id: + description: "Optional collection ID" + metabase_update_dashboard: + description: "Update a dashboard's name, description, or other properties" + parameters: + dashboard_id: + description: "Dashboard ID" + required: true + name: + description: "New name" + description: + description: "New description" + archived: + description: "Set to true to archive the dashboard" + metabase_delete_dashboard: + description: "Delete (archive) a dashboard" + parameters: + dashboard_id: + description: "Dashboard ID" + required: true + metabase_add_card_to_dashboard: + description: "Add a saved question/card to a dashboard" + parameters: + dashboard_id: + description: "Dashboard ID" + required: true + card_id: + description: "Card ID to add" + required: true + size_x: + description: "Width in grid units (default: 6)" + size_y: + description: "Height in grid units (default: 4)" + row: + description: "Row position (default: 0)" + col: + description: "Column position (default: 0)" + metabase_list_collections: + description: "List all collections (folders for organizing questions and dashboards)" + parameters: {} + metabase_get_collection: + description: "Get details and items in a specific collection" + parameters: + collection_id: + description: "Collection ID (use 'root' for the root collection)" + required: true + metabase_create_collection: + description: "Create a new collection for organizing questions and dashboards" + parameters: + name: + description: "Collection name" + required: true + description: + description: "Optional description" + parent_id: + description: "Optional parent collection ID for nesting" + metabase_update_collection: + description: "Update a collection's name, description, or parent" + parameters: + collection_id: + description: "Collection ID" + required: true + name: + description: "New name" + description: + description: "New description" + parent_id: + description: "New parent collection ID" + archived: + description: "Set to true to archive" + metabase_search: + description: "Search across all Metabase content (questions, dashboards, collections, tables, databases). Start here for BI and reporting workflows." + parameters: + query: + description: "Search query string" + required: true + models: + description: "Comma-separated types to search: card, dashboard, collection, table, database" diff --git a/integrations/nomad/tools.go b/integrations/nomad/tools.go index fdbf78e3..3f2af06f 100644 --- a/integrations/nomad/tools.go +++ b/integrations/nomad/tools.go @@ -1,140 +1,12 @@ package nomad -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Jobs ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_jobs"), Description: "List all jobs in the Nomad cluster. Start here for workload orchestration, container scheduling, and discovering running services.", - Parameters: map[string]string{"namespace": "Namespace (default: default)", "prefix": "Filter by job ID prefix", "filter": "Filter expression (e.g., Status == \"running\")"}, - }, - { - Name: mcp.ToolName("nomad_get_job"), Description: "Get full specification and status of a specific Nomad job, including task groups, constraints, and resource requirements. Use after list_jobs.", - Parameters: map[string]string{"job_id": "Job ID", "namespace": "Namespace (default: default)"}, - Required: []string{"job_id"}, - }, - { - Name: mcp.ToolName("nomad_get_job_versions"), Description: "Get version history for a Nomad job. Shows previous configurations and when changes were made.", - Parameters: map[string]string{"job_id": "Job ID", "namespace": "Namespace (default: default)"}, - Required: []string{"job_id"}, - }, - { - Name: mcp.ToolName("nomad_register_job"), Description: "Register (create or update) a Nomad job. Accepts a full job specification as JSON.", - Parameters: map[string]string{"job": "Full job specification as JSON object", "namespace": "Namespace (default: default)"}, - Required: []string{"job"}, - }, - { - Name: mcp.ToolName("nomad_stop_job"), Description: "Stop (deregister) a running Nomad job. All allocations will be stopped.", - Parameters: map[string]string{"job_id": "Job ID", "purge": "Completely purge the job from the system (true/false, default: false)", "namespace": "Namespace (default: default)"}, - Required: []string{"job_id"}, - }, - { - Name: mcp.ToolName("nomad_force_evaluate"), Description: "Force a new evaluation for a Nomad job, triggering rescheduling. Useful when allocations are unhealthy or stuck.", - Parameters: map[string]string{"job_id": "Job ID", "namespace": "Namespace (default: default)"}, - Required: []string{"job_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Allocations ────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_allocations"), Description: "List all allocations across the Nomad cluster. Shows where tasks are placed and their health status.", - Parameters: map[string]string{"namespace": "Namespace (default: default)", "prefix": "Filter by allocation ID prefix", "filter": "Filter expression"}, - }, - { - Name: mcp.ToolName("nomad_get_allocation"), Description: "Get details of a specific Nomad allocation, including task states, events, restart history, and resource usage.", - Parameters: map[string]string{"alloc_id": "Allocation ID"}, - Required: []string{"alloc_id"}, - }, - { - Name: mcp.ToolName("nomad_get_job_allocations"), Description: "List all allocations for a specific Nomad job. Shows placement, health, and status of each task instance.", - Parameters: map[string]string{"job_id": "Job ID", "namespace": "Namespace (default: default)"}, - Required: []string{"job_id"}, - }, - { - Name: mcp.ToolName("nomad_stop_allocation"), Description: "Stop a specific Nomad allocation. The scheduler may place a replacement depending on job configuration.", - Parameters: map[string]string{"alloc_id": "Allocation ID"}, - Required: []string{"alloc_id"}, - }, - { - Name: mcp.ToolName("nomad_restart_allocation"), Description: "Restart a task within a Nomad allocation. Optionally specify which task to restart.", - Parameters: map[string]string{"alloc_id": "Allocation ID", "task": "Task name (optional, restarts all tasks if omitted)"}, - Required: []string{"alloc_id"}, - }, - { - Name: mcp.ToolName("nomad_read_allocation_logs"), Description: "Read stdout or stderr logs from a task in a Nomad allocation. Use for debugging container and workload issues.", - Parameters: map[string]string{"alloc_id": "Allocation ID", "task": "Task name", "log_type": "Log type: stdout or stderr (default: stdout)", "plain": "Return plain text instead of JSON (true/false, default: true)", "origin": "Log origin: start or end (default: end)", "offset": "Byte offset to start reading from"}, - Required: []string{"alloc_id", "task"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Nodes ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_nodes"), Description: "List all client nodes in the Nomad cluster. Shows node status, datacenter, drivers, and scheduling eligibility.", - Parameters: map[string]string{"prefix": "Filter by node ID prefix", "filter": "Filter expression (e.g., Status == \"ready\")"}, - }, - { - Name: mcp.ToolName("nomad_get_node"), Description: "Get full details of a specific Nomad node, including attributes, resources, drivers, host volumes, and metadata.", - Parameters: map[string]string{"node_id": "Node ID"}, - Required: []string{"node_id"}, - }, - { - Name: mcp.ToolName("nomad_get_node_allocations"), Description: "List all allocations placed on a specific Nomad node. Shows what workloads are running on the node.", - Parameters: map[string]string{"node_id": "Node ID"}, - Required: []string{"node_id"}, - }, - { - Name: mcp.ToolName("nomad_drain_node"), Description: "Enable or disable drain mode on a Nomad node. Draining migrates all allocations off the node for maintenance.", - Parameters: map[string]string{"node_id": "Node ID", "enable": "Enable drain (true) or disable (false)", "deadline": "Drain deadline duration (e.g., '1h', '30m'). Use -1 for no deadline", "ignore_system_jobs": "Skip draining system jobs (true/false, default: false)"}, - Required: []string{"node_id", "enable"}, - }, - { - Name: mcp.ToolName("nomad_node_eligibility"), Description: "Toggle scheduling eligibility for a Nomad node. Ineligible nodes won't receive new allocations but keep existing ones.", - Parameters: map[string]string{"node_id": "Node ID", "eligible": "Set eligible (true) or ineligible (false)"}, - Required: []string{"node_id", "eligible"}, - }, - - // ── Deployments ────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_deployments"), Description: "List deployments across the Nomad cluster. Shows rolling update status, canary progress, and deployment health.", - Parameters: map[string]string{"namespace": "Namespace (default: default)", "prefix": "Filter by deployment ID prefix"}, - }, - { - Name: mcp.ToolName("nomad_get_deployment"), Description: "Get details of a specific Nomad deployment, including task group status, health, and canary information.", - Parameters: map[string]string{"deployment_id": "Deployment ID"}, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("nomad_promote_deployment"), Description: "Promote canary allocations in a Nomad deployment. Moves canaries to production after validation.", - Parameters: map[string]string{"deployment_id": "Deployment ID", "all": "Promote all task groups (true/false, default: true)", "groups": "Comma-separated list of task groups to promote (alternative to all)"}, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("nomad_fail_deployment"), Description: "Mark a Nomad deployment as failed, triggering automatic rollback to the previous job version.", - Parameters: map[string]string{"deployment_id": "Deployment ID"}, - Required: []string{"deployment_id"}, - }, - - // ── Evaluations ────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_evaluations"), Description: "List evaluations in the Nomad scheduler queue. Shows scheduling decisions, blocked evaluations, and failures.", - Parameters: map[string]string{"namespace": "Namespace (default: default)", "prefix": "Filter by evaluation ID prefix", "filter": "Filter expression (e.g., Status == \"blocked\")"}, - }, - - // ── Services ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_list_services"), Description: "List all registered services in the Nomad service discovery catalog.", - Parameters: map[string]string{"namespace": "Namespace (default: default)"}, - }, - - // ── Cluster ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("nomad_get_agent_self"), Description: "Get the current Nomad agent's configuration, stats, and node information. Useful for diagnosing agent state.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("nomad_get_cluster_status"), Description: "Get Nomad cluster status including Raft leader address and peer list. Use for cluster health checks.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("nomad_gc"), Description: "Trigger garbage collection on the Nomad cluster to clean up dead allocations, evaluations, and deployments.", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/nomad/tools.yaml b/integrations/nomad/tools.yaml new file mode 100644 index 00000000..4a9cf73b --- /dev/null +++ b/integrations/nomad/tools.yaml @@ -0,0 +1,200 @@ +version: 1 +tools: + nomad_list_jobs: + description: "List all jobs in the Nomad cluster. Start here for workload orchestration, container scheduling, and discovering running services." + parameters: + namespace: + description: "Namespace (default: default)" + prefix: + description: "Filter by job ID prefix" + filter: + description: 'Filter expression (e.g., Status == "running")' + nomad_get_job: + description: "Get full specification and status of a specific Nomad job, including task groups, constraints, and resource requirements. Use after list_jobs." + parameters: + job_id: + description: "Job ID" + required: true + namespace: + description: "Namespace (default: default)" + nomad_get_job_versions: + description: "Get version history for a Nomad job. Shows previous configurations and when changes were made." + parameters: + job_id: + description: "Job ID" + required: true + namespace: + description: "Namespace (default: default)" + nomad_register_job: + description: "Register (create or update) a Nomad job. Accepts a full job specification as JSON." + parameters: + job: + description: "Full job specification as JSON object" + required: true + namespace: + description: "Namespace (default: default)" + nomad_stop_job: + description: "Stop (deregister) a running Nomad job. All allocations will be stopped." + parameters: + job_id: + description: "Job ID" + required: true + purge: + description: "Completely purge the job from the system (true/false, default: false)" + namespace: + description: "Namespace (default: default)" + nomad_force_evaluate: + description: "Force a new evaluation for a Nomad job, triggering rescheduling. Useful when allocations are unhealthy or stuck." + parameters: + job_id: + description: "Job ID" + required: true + namespace: + description: "Namespace (default: default)" + nomad_list_allocations: + description: "List all allocations across the Nomad cluster. Shows where tasks are placed and their health status." + parameters: + namespace: + description: "Namespace (default: default)" + prefix: + description: "Filter by allocation ID prefix" + filter: + description: "Filter expression" + nomad_get_allocation: + description: "Get details of a specific Nomad allocation, including task states, events, restart history, and resource usage." + parameters: + alloc_id: + description: "Allocation ID" + required: true + nomad_get_job_allocations: + description: "List all allocations for a specific Nomad job. Shows placement, health, and status of each task instance." + parameters: + job_id: + description: "Job ID" + required: true + namespace: + description: "Namespace (default: default)" + nomad_stop_allocation: + description: "Stop a specific Nomad allocation. The scheduler may place a replacement depending on job configuration." + parameters: + alloc_id: + description: "Allocation ID" + required: true + nomad_restart_allocation: + description: "Restart a task within a Nomad allocation. Optionally specify which task to restart." + parameters: + alloc_id: + description: "Allocation ID" + required: true + task: + description: "Task name (optional, restarts all tasks if omitted)" + nomad_read_allocation_logs: + description: "Read stdout or stderr logs from a task in a Nomad allocation. Use for debugging container and workload issues." + parameters: + alloc_id: + description: "Allocation ID" + required: true + task: + description: "Task name" + required: true + log_type: + description: "Log type: stdout or stderr (default: stdout)" + plain: + description: "Return plain text instead of JSON (true/false, default: true)" + origin: + description: "Log origin: start or end (default: end)" + offset: + description: "Byte offset to start reading from" + nomad_list_nodes: + description: 'List all client nodes in the Nomad cluster. Shows node status, datacenter, drivers, and scheduling eligibility.' + parameters: + prefix: + description: "Filter by node ID prefix" + filter: + description: 'Filter expression (e.g., Status == "ready")' + nomad_get_node: + description: "Get full details of a specific Nomad node, including attributes, resources, drivers, host volumes, and metadata." + parameters: + node_id: + description: "Node ID" + required: true + nomad_get_node_allocations: + description: "List all allocations placed on a specific Nomad node. Shows what workloads are running on the node." + parameters: + node_id: + description: "Node ID" + required: true + nomad_drain_node: + description: "Enable or disable drain mode on a Nomad node. Draining migrates all allocations off the node for maintenance." + parameters: + node_id: + description: "Node ID" + required: true + enable: + description: "Enable drain (true) or disable (false)" + required: true + deadline: + description: "Drain deadline duration (e.g., '1h', '30m'). Use -1 for no deadline" + ignore_system_jobs: + description: "Skip draining system jobs (true/false, default: false)" + nomad_node_eligibility: + description: "Toggle scheduling eligibility for a Nomad node. Ineligible nodes won't receive new allocations but keep existing ones." + parameters: + node_id: + description: "Node ID" + required: true + eligible: + description: "Set eligible (true) or ineligible (false)" + required: true + nomad_list_deployments: + description: "List deployments across the Nomad cluster. Shows rolling update status, canary progress, and deployment health." + parameters: + namespace: + description: "Namespace (default: default)" + prefix: + description: "Filter by deployment ID prefix" + nomad_get_deployment: + description: "Get details of a specific Nomad deployment, including task group status, health, and canary information." + parameters: + deployment_id: + description: "Deployment ID" + required: true + nomad_promote_deployment: + description: "Promote canary allocations in a Nomad deployment. Moves canaries to production after validation." + parameters: + deployment_id: + description: "Deployment ID" + required: true + all: + description: "Promote all task groups (true/false, default: true)" + groups: + description: "Comma-separated list of task groups to promote (alternative to all)" + nomad_fail_deployment: + description: "Mark a Nomad deployment as failed, triggering automatic rollback to the previous job version." + parameters: + deployment_id: + description: "Deployment ID" + required: true + nomad_list_evaluations: + description: "List evaluations in the Nomad scheduler queue. Shows scheduling decisions, blocked evaluations, and failures." + parameters: + namespace: + description: "Namespace (default: default)" + prefix: + description: "Filter by evaluation ID prefix" + filter: + description: 'Filter expression (e.g., Status == "blocked")' + nomad_list_services: + description: "List all registered services in the Nomad service discovery catalog." + parameters: + namespace: + description: "Namespace (default: default)" + nomad_get_agent_self: + description: "Get the current Nomad agent's configuration, stats, and node information. Useful for diagnosing agent state." + parameters: {} + nomad_get_cluster_status: + description: "Get Nomad cluster status including Raft leader address and peer list. Use for cluster health checks." + parameters: {} + nomad_gc: + description: "Trigger garbage collection on the Nomad cluster to clean up dead allocations, evaluations, and deployments." + parameters: {} diff --git a/integrations/notion/tools.go b/integrations/notion/tools.go index 011298f1..17a737d4 100644 --- a/integrations/notion/tools.go +++ b/integrations/notion/tools.go @@ -1,241 +1,18 @@ package notion -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // --- Data Sources --- - { - Name: mcp.ToolName("notion_create_database"), - Description: "Create a new database under a parent page", - Parameters: map[string]string{ - "parent": "Parent object with page_id (e.g. {\"page_id\": \"...\"})", - "title": "Title of the database (rich text array)", - "properties": "Property schema object defining columns and their types", - "is_inline": "Set to true to create an inline database (default false)", - }, - Required: []string{"parent"}, - }, - { - Name: mcp.ToolName("notion_retrieve_data_source"), - Description: "Retrieve a data source's property schema. Use before query_data_source to understand available columns, types, and filter options.", - Parameters: map[string]string{ - "data_source_id": "Block ID of the data source (the id field from search results — NOT collection_id)", - }, - Required: []string{"data_source_id"}, - }, - { - Name: mcp.ToolName("notion_update_data_source"), - Description: "Update a data source's title or property schema", - Parameters: map[string]string{ - "data_source_id": "Block ID of the data source (the id field from search results — NOT collection_id)", - "title": "New title (rich text array)", - "properties": "Updated property schema object", - }, - Required: []string{"data_source_id"}, - }, - { - Name: mcp.ToolName("notion_query_data_source"), - Description: "Query a data source (database) with optional filters and sorts, returning paginated rows. Use retrieve_data_source first to see the schema.", - Parameters: map[string]string{ - "data_source_id": "Block ID of the data source (the id field from search results — NOT collection_id). The handler resolves the collection internally.", - "filter": "Filter object to narrow results. Use retrieve_data_source to see available property names and types for building filters.", - "sorts": "Array of sort objects (property + direction)", - "start_cursor": "Cursor for pagination", - "page_size": "Number of results per page (max 100)", - }, - Required: []string{"data_source_id"}, - }, - { - Name: mcp.ToolName("notion_list_data_source_templates"), - Description: "List available templates for a data source", - Parameters: map[string]string{ - "data_source_id": "Block ID of the data source (the id field from search results — NOT collection_id)", - }, - Required: []string{"data_source_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // --- Databases --- - { - Name: mcp.ToolName("notion_retrieve_database"), - Description: "Retrieve a database by block ID. Equivalent to retrieve_data_source — both accept the block ID.", - Parameters: map[string]string{ - "database_id": "ID of the database", - }, - Required: []string{"database_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Pages --- - { - Name: mcp.ToolName("notion_create_page"), - Description: "Create a new page or database row with properties only (no content blocks). page_id parent creates a subpage; database_id parent creates a row. For pages with content, prefer create_page_with_content.", - Parameters: map[string]string{ - "parent": `Parent: {"page_id": "..."} for subpage, or {"database_id": ""} for database row. Use collection_id from search results, NOT the search result id field`, - "properties": "Page property values object", - "title": "Page title (convenience — sets the title property)", - }, - Required: []string{"parent"}, - }, - { - Name: mcp.ToolName("notion_retrieve_page"), - Description: "Retrieve a page's metadata and properties only. For full page content, prefer get_page_content.", - Parameters: map[string]string{ - "page_id": "ID of the page", - }, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("notion_update_page"), - Description: "Update a page's property values (status, assignee, dates, etc). Does not modify page content blocks — use append_block_children or update_block for that.", - Parameters: map[string]string{ - "page_id": "ID of the page to update", - "properties": "Updated property values object", - "archived": "Set to true to archive the page", - }, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("notion_move_page"), - Description: "Move a page to a new parent page or database", - Parameters: map[string]string{ - "page_id": "ID of the page to move", - "parent": "New parent object with page_id or database_id", - }, - Required: []string{"page_id", "parent"}, - }, - { - Name: mcp.ToolName("notion_retrieve_page_property"), - Description: "Retrieve a single property value. Rarely needed — retrieve_page returns all properties at once.", - Parameters: map[string]string{ - "page_id": "ID of the page", - "property_id": "ID or name of the property to retrieve", - }, - Required: []string{"page_id", "property_id"}, - }, - - // --- Blocks --- - { - Name: mcp.ToolName("notion_retrieve_block"), - Description: "Retrieve a single block by ID. For full page content, prefer get_page_content.", - Parameters: map[string]string{ - "block_id": "ID of the block", - }, - Required: []string{"block_id"}, - }, - { - Name: mcp.ToolName("notion_update_block"), - Description: "Update a block's content", - Parameters: map[string]string{ - "block_id": "ID of the block to update", - "type_content": "Block type-specific content object", - "archived": "Set to true to archive the block", - }, - Required: []string{"block_id"}, - }, - { - Name: mcp.ToolName("notion_delete_block"), - Description: "Delete a block by ID (marks as not alive)", - Parameters: map[string]string{ - "block_id": "ID of the block to delete", - }, - Required: []string{"block_id"}, - }, - { - Name: mcp.ToolName("notion_get_block_children"), - Description: "List immediate child blocks of a block. For full page tree, prefer get_page_content.", - Parameters: map[string]string{ - "block_id": "ID of the parent block", - }, - Required: []string{"block_id"}, - }, - { - Name: mcp.ToolName("notion_append_block_children"), - Description: "Append new child blocks to a page or block. Use for adding content to existing pages.", - Parameters: map[string]string{ - "block_id": "ID of the parent block", - "children": "Array of v3 block objects: {\"type\": \"text\", \"properties\": {\"title\": [[\"content\"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {\"code_language\": \"Python\"}), divider, toggle", - }, - Required: []string{"block_id", "children"}, - }, - - // --- Search --- - { - Name: mcp.ToolName("notion_search"), - Description: "Search across all pages and data sources in the workspace. Start here for most workflows. For database results: use id (block ID) for retrieve_data_source and query_data_source; use collection_id for creating rows via create_page with database_id parent.", - Parameters: map[string]string{ - "query": "Search query text. Searches page titles and content.", - "type": "Filter by type: \"page\" or \"data_source\"", - "limit": "Maximum number of results (default 20)", - "sort": "Sort object with field and direction", - "filters": "Additional filter object for v3 search", - "space_id": "Space ID (auto-filled if not provided)", - }, - }, - - // --- Users --- - { - Name: mcp.ToolName("notion_list_users"), - Description: "List all users in the workspace", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("notion_retrieve_user"), - Description: "Retrieve a user by ID", - Parameters: map[string]string{ - "user_id": "ID of the user", - }, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("notion_get_self"), - Description: "Retrieve the current authenticated user's ID and settings", - Parameters: map[string]string{}, - }, - - // --- Comments --- - { - Name: mcp.ToolName("notion_create_comment"), - Description: "Create a comment on a page or in an existing discussion thread. Provide page_id for a new discussion, or discussion_id to reply to an existing thread.", - Parameters: map[string]string{ - "page_id": "ID of the page (required for new discussion threads, omit when replying via discussion_id)", - "text": "Plain text content of the comment", - "discussion_id": "ID of an existing discussion thread (from retrieve_comments). Omit for new discussions — use page_id instead.", - }, - Required: []string{"text"}, - }, - { - Name: mcp.ToolName("notion_retrieve_comments"), - Description: "Retrieve all comment threads on a page. Returns discussions with their comments.", - Parameters: map[string]string{ - "block_id": "ID of the block or page", - }, - Required: []string{"block_id"}, - }, - - // --- Convenience --- - { - Name: mcp.ToolName("notion_get_page_content"), - Description: "Retrieve a page and all its block content in one call. Preferred over retrieve_page — returns the full page tree, not just metadata.", - Parameters: map[string]string{ - "page_id": "ID of the page (from search results or a known page URL)", - "limit": "Maximum number of blocks to load (default 100)", - }, - Required: []string{"page_id"}, - }, - { - Name: mcp.ToolName("notion_create_page_with_content"), - Description: "Create a page or database row with properties and block content in a single atomic transaction. Preferred over create_page + append_block_children — fewer calls, atomic. page_id parent creates a subpage; database_id parent creates a row.", - Parameters: map[string]string{ - "parent": `Parent: {"page_id": "..."} for subpage, or {"database_id": ""} for database row. Use collection_id from search results, NOT the search result id field`, - "properties": "Page property values object", - "title": "Page title (convenience)", - "children": "Array of v3 block objects: {\"type\": \"text\", \"properties\": {\"title\": [[\"content\"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {\"code_language\": \"Python\"}), divider, toggle", - }, - Required: []string{"parent", "children"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ - // Data Sources + mcp.ToolName("notion_create_database"): createDatabase, mcp.ToolName("notion_retrieve_data_source"): retrieveDataSource, mcp.ToolName("notion_update_data_source"): updateDataSource, diff --git a/integrations/notion/tools.yaml b/integrations/notion/tools.yaml new file mode 100644 index 00000000..beb30715 --- /dev/null +++ b/integrations/notion/tools.yaml @@ -0,0 +1,201 @@ +version: 1 +tools: + notion_create_database: + description: "Create a new database under a parent page" + parameters: + parent: + description: 'Parent object with page_id (e.g. {"page_id": "..."})' + required: true + title: + description: "Title of the database (rich text array)" + properties: + description: "Property schema object defining columns and their types" + is_inline: + description: "Set to true to create an inline database (default false)" + notion_retrieve_data_source: + description: "Retrieve a data source's property schema. Use before query_data_source to understand available columns, types, and filter options." + parameters: + data_source_id: + description: "Block ID of the data source (the id field from search results — NOT collection_id)" + required: true + notion_update_data_source: + description: "Update a data source's title or property schema" + parameters: + data_source_id: + description: "Block ID of the data source (the id field from search results — NOT collection_id)" + required: true + title: + description: "New title (rich text array)" + properties: + description: "Updated property schema object" + notion_query_data_source: + description: "Query a data source (database) with optional filters and sorts, returning paginated rows. Use retrieve_data_source first to see the schema." + parameters: + data_source_id: + description: "Block ID of the data source (the id field from search results — NOT collection_id). The handler resolves the collection internally." + required: true + filter: + description: "Filter object to narrow results. Use retrieve_data_source to see available property names and types for building filters." + sorts: + description: "Array of sort objects (property + direction)" + start_cursor: + description: "Cursor for pagination" + page_size: + description: "Number of results per page (max 100)" + notion_list_data_source_templates: + description: "List available templates for a data source" + parameters: + data_source_id: + description: "Block ID of the data source (the id field from search results — NOT collection_id)" + required: true + notion_retrieve_database: + description: "Retrieve a database by block ID. Equivalent to retrieve_data_source — both accept the block ID." + parameters: + database_id: + description: "ID of the database" + required: true + notion_create_page: + description: "Create a new page or database row with properties only (no content blocks). page_id parent creates a subpage; database_id parent creates a row. For pages with content, prefer create_page_with_content." + parameters: + parent: + description: 'Parent: {"page_id": "..."} for subpage, or {"database_id": ""} for database row. Use collection_id from search results, NOT the search result id field' + required: true + properties: + description: "Page property values object" + title: + description: "Page title (convenience — sets the title property)" + notion_retrieve_page: + description: "Retrieve a page's metadata and properties only. For full page content, prefer get_page_content." + parameters: + page_id: + description: "ID of the page" + required: true + notion_update_page: + description: "Update a page's property values (status, assignee, dates, etc). Does not modify page content blocks — use append_block_children or update_block for that." + parameters: + page_id: + description: "ID of the page to update" + required: true + properties: + description: "Updated property values object" + archived: + description: "Set to true to archive the page" + notion_move_page: + description: "Move a page to a new parent page or database" + parameters: + page_id: + description: "ID of the page to move" + required: true + parent: + description: "New parent object with page_id or database_id" + required: true + notion_retrieve_page_property: + description: "Retrieve a single property value. Rarely needed — retrieve_page returns all properties at once." + parameters: + page_id: + description: "ID of the page" + required: true + property_id: + description: "ID or name of the property to retrieve" + required: true + notion_retrieve_block: + description: "Retrieve a single block by ID. For full page content, prefer get_page_content." + parameters: + block_id: + description: "ID of the block" + required: true + notion_update_block: + description: "Update a block's content" + parameters: + block_id: + description: "ID of the block to update" + required: true + type_content: + description: "Block type-specific content object" + archived: + description: "Set to true to archive the block" + notion_delete_block: + description: "Delete a block by ID (marks as not alive)" + parameters: + block_id: + description: "ID of the block to delete" + required: true + notion_get_block_children: + description: "List immediate child blocks of a block. For full page tree, prefer get_page_content." + parameters: + block_id: + description: "ID of the parent block" + required: true + notion_append_block_children: + description: 'Append new child blocks to a page or block. Use for adding content to existing pages.' + parameters: + block_id: + description: "ID of the parent block" + required: true + children: + description: 'Array of v3 block objects: {"type": "text", "properties": {"title": [["content"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {"code_language": "Python"}), divider, toggle' + required: true + notion_search: + description: "Search across all pages and data sources in the workspace. Start here for most workflows. For database results: use id (block ID) for retrieve_data_source and query_data_source; use collection_id for creating rows via create_page with database_id parent." + parameters: + query: + description: "Search query text. Searches page titles and content." + type: + description: 'Filter by type: "page" or "data_source"' + limit: + description: "Maximum number of results (default 20)" + sort: + description: "Sort object with field and direction" + filters: + description: "Additional filter object for v3 search" + space_id: + description: "Space ID (auto-filled if not provided)" + notion_list_users: + description: "List all users in the workspace" + parameters: {} + notion_retrieve_user: + description: "Retrieve a user by ID" + parameters: + user_id: + description: "ID of the user" + required: true + notion_get_self: + description: "Retrieve the current authenticated user's ID and settings" + parameters: {} + notion_create_comment: + description: "Create a comment on a page or in an existing discussion thread. Provide page_id for a new discussion, or discussion_id to reply to an existing thread." + parameters: + page_id: + description: "ID of the page (required for new discussion threads, omit when replying via discussion_id)" + text: + description: "Plain text content of the comment" + required: true + discussion_id: + description: "ID of an existing discussion thread (from retrieve_comments). Omit for new discussions — use page_id instead." + notion_retrieve_comments: + description: "Retrieve all comment threads on a page. Returns discussions with their comments." + parameters: + block_id: + description: "ID of the block or page" + required: true + notion_get_page_content: + description: "Retrieve a page and all its block content in one call. Preferred over retrieve_page — returns the full page tree, not just metadata." + parameters: + page_id: + description: "ID of the page (from search results or a known page URL)" + required: true + limit: + description: "Maximum number of blocks to load (default 100)" + notion_create_page_with_content: + description: "Create a page or database row with properties and block content in a single atomic transaction. Preferred over create_page + append_block_children — fewer calls, atomic. page_id parent creates a subpage; database_id parent creates a row." + parameters: + parent: + description: 'Parent: {"page_id": "..."} for subpage, or {"database_id": ""} for database row. Use collection_id from search results, NOT the search result id field' + required: true + properties: + description: "Page property values object" + title: + description: "Page title (convenience)" + children: + description: 'Array of v3 block objects: {"type": "text", "properties": {"title": [["content"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {"code_language": "Python"}), divider, toggle' + required: true diff --git a/integrations/ollama/tools.go b/integrations/ollama/tools.go index 0756f0ba..8e1296a9 100644 --- a/integrations/ollama/tools.go +++ b/integrations/ollama/tools.go @@ -1,113 +1,12 @@ package ollama -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Model Management ──────────────────────────────────────────── - { - Name: "ollama_list_models", - Description: "List all locally installed Ollama AI models with sizes, families, parameter counts, and quantization levels. Start here to discover available models before running chat, generate, or embed.", - Parameters: map[string]string{}, - }, - { - Name: "ollama_show_model", - Description: "Show details of a specific Ollama model including parameters, prompt template, capabilities (vision, tools, thinking), and license. Returns rendered markdown. Use after list_models to inspect a model before using it.", - Parameters: map[string]string{ - "model": "Model name (e.g. 'gemma3', 'llama3.2:7b'). Use list_models to discover available names.", - }, - Required: []string{"model"}, - }, - { - Name: "ollama_pull_model", - Description: "Download and install an AI model from the Ollama model registry. May take minutes for large models — do not retry on timeout. Use model names like 'gemma3', 'llama3.2', 'qwen3'. Use list_models after to confirm the download completed.", - Parameters: map[string]string{ - "model": "Model name to download from registry (e.g. 'gemma3', 'llama3.2:7b', 'qwen3:14b')", - }, - Required: []string{"model"}, - }, - { - Name: "ollama_delete_model", - Description: "Permanently delete a locally installed Ollama model. This is irreversible — the model must be re-downloaded with pull_model to restore it. Use list_models first to verify the exact model name.", - Parameters: map[string]string{ - "model": "Exact model name to delete. Must match a name from list_models.", - }, - Required: []string{"model"}, - }, - { - Name: "ollama_copy_model", - Description: "Create a copy of an existing local Ollama model with a new name. Use before create_model to preserve the original when customizing.", - Parameters: map[string]string{ - "source": "Existing model name to copy from (must be installed locally)", - "destination": "New model name to create", - }, - Required: []string{"source", "destination"}, - }, - { - Name: "ollama_create_model", - Description: "Create a custom Ollama model from an existing base model. Embed a system prompt, override the prompt template, or change quantization. May take minutes for re-quantization — do not retry on timeout. Use show_model after to verify the result.", - Parameters: map[string]string{ - "model": "Name for the new custom model", - "from": "Base model to derive from (e.g. 'gemma3'). Must be installed locally.", - "system": "System prompt to embed permanently in the model", - "template": "Go template string for prompt formatting", - "quantize": "Quantization level to apply (e.g. 'q4_K_M', 'q8_0'). Re-quantizes the model weights.", - }, - Required: []string{"model"}, - }, - { - Name: "ollama_list_running", - Description: "List currently loaded and running Ollama models with VRAM usage, context window size, and automatic unload time. Use to check GPU memory pressure before loading additional models.", - Parameters: map[string]string{}, - }, - { - Name: "ollama_get_version", - Description: "Get the Ollama server version. Use to verify the server is reachable and check compatibility.", - Parameters: map[string]string{}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Inference ─────────────────────────────────────────────────── - { - Name: "ollama_chat", - Description: "Send a multi-turn chat conversation to a local Ollama model and get a complete response. Preferred over generate for conversations — maintains message history with roles. Supports tool calling, structured JSON output, vision (images in messages), and thinking/reasoning mode. Returns the full response in one call (non-streaming).", - Parameters: map[string]string{ - "model": "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check.", - "messages": "Array of message objects. Each has 'role' ('system', 'user', 'assistant') and 'content' (string). For vision: add 'images' array with base64-encoded strings.", - "tools": "Array of tool/function definitions the model may call (OpenAI function-calling format)", - "format": "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output", - "options": "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc.", - "think": "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled.", - "keep_alive": "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes.", - }, - Required: []string{"model", "messages"}, - }, - { - Name: "ollama_generate", - Description: "Generate text completion from a single prompt using a local Ollama model. Use for one-shot generation without conversation history — prefer chat for multi-turn dialogue. Supports vision (base64 images), fill-in-the-middle (prompt + suffix), structured JSON output, and thinking mode. Returns the full response in one call (non-streaming).", - Parameters: map[string]string{ - "model": "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check.", - "prompt": "Text prompt for generation", - "suffix": "Text after the insertion point for fill-in-the-middle completion. Model generates text between prompt and suffix.", - "images": "Array of base64-encoded images for multimodal/vision models (e.g. llava, gemma4)", - "format": "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output", - "system": "System prompt to prepend. Overrides any system prompt embedded in the model.", - "options": "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc.", - "think": "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled.", - "raw": "If true, prompt is sent directly without applying the model's chat template. Use for pre-formatted prompts only.", - "keep_alive": "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes.", - }, - Required: []string{"model"}, - }, - { - Name: "ollama_embed", - Description: "Generate vector embeddings for text using a local Ollama embedding model. For semantic search, similarity matching, and RAG pipelines. Supports single string or batch (array of strings) input. Not all models support embeddings — use an embedding model like 'all-minilm' or 'nomic-embed-text'. Returns 400 error if the model lacks embedding support.", - Parameters: map[string]string{ - "model": "Embedding model name (e.g. 'all-minilm', 'nomic-embed-text'). Must support embeddings — general chat models will return an error.", - "input": "Text to embed: a single string, or an array of strings for batch embedding", - "truncate": "Truncate input exceeding context window (default true). Set false to return an error instead of silently truncating.", - "dimensions": "Custom output vector dimensions. Only supported by some models.", - "keep_alive": "Duration to keep model in memory: '5m', '1h', or 0 to unload immediately", - "options": "Model parameters: seed, num_ctx, etc.", - }, - Required: []string{"model", "input"}, - }, -} +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/ollama/tools.yaml b/integrations/ollama/tools.yaml new file mode 100644 index 00000000..9fc93171 --- /dev/null +++ b/integrations/ollama/tools.yaml @@ -0,0 +1,112 @@ +version: 1 +tools: + ollama_list_models: + description: "List all locally installed Ollama AI models with sizes, families, parameter counts, and quantization levels. Start here to discover available models before running chat, generate, or embed." + parameters: {} + ollama_show_model: + description: "Show details of a specific Ollama model including parameters, prompt template, capabilities (vision, tools, thinking), and license. Returns rendered markdown. Use after list_models to inspect a model before using it." + parameters: + model: + description: "Model name (e.g. 'gemma3', 'llama3.2:7b'). Use list_models to discover available names." + required: true + ollama_pull_model: + description: "Download and install an AI model from the Ollama model registry. May take minutes for large models — do not retry on timeout. Use model names like 'gemma3', 'llama3.2', 'qwen3'. Use list_models after to confirm the download completed." + parameters: + model: + description: "Model name to download from registry (e.g. 'gemma3', 'llama3.2:7b', 'qwen3:14b')" + required: true + ollama_delete_model: + description: "Permanently delete a locally installed Ollama model. This is irreversible — the model must be re-downloaded with pull_model to restore it. Use list_models first to verify the exact model name." + parameters: + model: + description: "Exact model name to delete. Must match a name from list_models." + required: true + ollama_copy_model: + description: "Create a copy of an existing local Ollama model with a new name. Use before create_model to preserve the original when customizing." + parameters: + source: + description: "Existing model name to copy from (must be installed locally)" + required: true + destination: + description: "New model name to create" + required: true + ollama_create_model: + description: "Create a custom Ollama model from an existing base model. Embed a system prompt, override the prompt template, or change quantization. May take minutes for re-quantization — do not retry on timeout. Use show_model after to verify the result." + parameters: + model: + description: "Name for the new custom model" + required: true + from: + description: "Base model to derive from (e.g. 'gemma3'). Must be installed locally." + system: + description: "System prompt to embed permanently in the model" + template: + description: "Go template string for prompt formatting" + quantize: + description: "Quantization level to apply (e.g. 'q4_K_M', 'q8_0'). Re-quantizes the model weights." + ollama_list_running: + description: "List currently loaded and running Ollama models with VRAM usage, context window size, and automatic unload time. Use to check GPU memory pressure before loading additional models." + parameters: {} + ollama_get_version: + description: "Get the Ollama server version. Use to verify the server is reachable and check compatibility." + parameters: {} + ollama_chat: + description: "Send a multi-turn chat conversation to a local Ollama model and get a complete response. Preferred over generate for conversations — maintains message history with roles. Supports tool calling, structured JSON output, vision (images in messages), and thinking/reasoning mode. Returns the full response in one call (non-streaming)." + parameters: + model: + description: "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check." + required: true + messages: + description: "Array of message objects. Each has 'role' ('system', 'user', 'assistant') and 'content' (string). For vision: add 'images' array with base64-encoded strings." + required: true + tools: + description: "Array of tool/function definitions the model may call (OpenAI function-calling format)" + format: + description: "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output" + options: + description: "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc." + think: + description: "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled." + keep_alive: + description: "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes." + ollama_generate: + description: "Generate text completion from a single prompt using a local Ollama model. Use for one-shot generation without conversation history — prefer chat for multi-turn dialogue. Supports vision (base64 images), fill-in-the-middle (prompt + suffix), structured JSON output, and thinking mode. Returns the full response in one call (non-streaming)." + parameters: + model: + description: "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check." + required: true + prompt: + description: "Text prompt for generation" + suffix: + description: "Text after the insertion point for fill-in-the-middle completion. Model generates text between prompt and suffix." + images: + description: "Array of base64-encoded images for multimodal/vision models (e.g. llava, gemma4)" + format: + description: "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output" + system: + description: "System prompt to prepend. Overrides any system prompt embedded in the model." + options: + description: "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc." + think: + description: "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled." + raw: + description: "If true, prompt is sent directly without applying the model's chat template. Use for pre-formatted prompts only." + keep_alive: + description: "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes." + ollama_embed: + description: "Generate vector embeddings for text using a local Ollama embedding model. For semantic search, similarity matching, and RAG pipelines. Supports single string or batch (array of strings) input. Not all models support embeddings — use an embedding model like 'all-minilm' or 'nomic-embed-text'. Returns 400 error if the model lacks embedding support." + parameters: + model: + description: "Embedding model name (e.g. 'all-minilm', 'nomic-embed-text'). Must support embeddings — general chat models will return an error." + required: true + input: + description: "Text to embed: a single string, or an array of strings for batch embedding" + required: true + truncate: + description: "Truncate input exceeding context window (default true). Set false to return an error instead of silently truncating." + dimensions: + description: "Custom output vector dimensions. Only supported by some models." + keep_alive: + description: "Duration to keep model in memory: '5m', '1h', or 0 to unload immediately" + options: + description: "Model parameters: seed, num_ctx, etc." diff --git a/integrations/pganalyze/pganalyze_test.go b/integrations/pganalyze/pganalyze_test.go index a67d612e..b48613d0 100644 --- a/integrations/pganalyze/pganalyze_test.go +++ b/integrations/pganalyze/pganalyze_test.go @@ -279,10 +279,22 @@ func TestProxy_ToolDefinitions_RequiredFields(t *testing.T) { p, _ := newTestPganalyze(t) for _, tool := range p.Tools() { if tool.Name == "pganalyze_get_server_details" { - assert.Contains(t, tool.Required, "server_id") + found := false + for _, param := range tool.Parameters { + if string(param.Name) == "server_id" && param.Required { + found = true + } + } + assert.True(t, found, "pganalyze_get_server_details: server_id not required") } if tool.Name == "pganalyze_get_query_stats" { - assert.Contains(t, tool.Required, "database_id") + found := false + for _, param := range tool.Parameters { + if string(param.Name) == "database_id" && param.Required { + found = true + } + } + assert.True(t, found, "pganalyze_get_query_stats: database_id not required") } } } diff --git a/integrations/pganalyze/proxy.go b/integrations/pganalyze/proxy.go index 44234b50..c0ac2ed6 100644 --- a/integrations/pganalyze/proxy.go +++ b/integrations/pganalyze/proxy.go @@ -157,9 +157,16 @@ func (p *proxyClient) toolDefinitions() []mcp.ToolDefinition { defs := make([]mcp.ToolDefinition, 0, len(p.tools)) for _, t := range p.tools { - params := make(map[string]string) - var requiredFields []string + required := make(map[string]bool) + if reqList, ok := t.InputSchema["required"].([]any); ok { + for _, r := range reqList { + if s, ok := r.(string); ok { + required[s] = true + } + } + } + var params []mcp.Parameter if props, ok := t.InputSchema["properties"].(map[string]any); ok { for key, val := range props { desc := "" @@ -168,25 +175,18 @@ func (p *proxyClient) toolDefinitions() []mcp.ToolDefinition { desc = d } } - params[key] = desc + params = append(params, mcp.Parameter{ + Name: mcp.ParamName(key), + Description: desc, + Required: required[key], + }) } } - if reqList, ok := t.InputSchema["required"].([]any); ok { - for _, r := range reqList { - if s, ok := r.(string); ok { - requiredFields = append(requiredFields, s) - } - } - } - - name := mcp.ToolName("pganalyze_" + t.Name) - desc := t.Description defs = append(defs, mcp.ToolDefinition{ - Name: name, - Description: desc, + Name: mcp.ToolName("pganalyze_" + t.Name), + Description: t.Description, Parameters: params, - Required: requiredFields, }) } return defs diff --git a/integrations/pganalyze/tools.go b/integrations/pganalyze/tools.go index e157cf5c..2cc565eb 100644 --- a/integrations/pganalyze/tools.go +++ b/integrations/pganalyze/tools.go @@ -1,194 +1,15 @@ package pganalyze -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" + + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte // staticTools defines the known pganalyze MCP tool definitions. // These are used for search indexing when the proxy has not yet connected. // When the proxy connects successfully, tools are dynamically refreshed from the MCP server. -var staticTools = []mcp.ToolDefinition{ - // --- Servers --- - { - Name: mcp.ToolName("pganalyze_list_servers"), - Description: "List monitored PostgreSQL servers in pganalyze. Start here to discover server IDs and database IDs needed by other tools.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("pganalyze_get_server_details"), - Description: "Get details for a specific monitored PostgreSQL server including configuration and snapshot info.", - Parameters: map[string]string{ - "server_id": "The server ID (from pganalyze_list_servers)", - }, - Required: []string{"server_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_postgres_settings"), - Description: "Get PostgreSQL configuration settings (e.g. shared_buffers, work_mem) for a server.", - Parameters: map[string]string{ - "server_id": "The server ID (from pganalyze_list_servers)", - }, - Required: []string{"server_id"}, - }, - - // --- Databases --- - { - Name: mcp.ToolName("pganalyze_get_databases"), - Description: "List databases with size stats and issue counts for a server.", - Parameters: map[string]string{ - "server_id": "The server ID (from pganalyze_list_servers)", - }, - Required: []string{"server_id"}, - }, - - // --- Queries --- - { - Name: mcp.ToolName("pganalyze_get_query_stats"), - Description: "Get top queries by runtime percentage. Shows expensive and slow query bottlenecks sorted by impact.", - Parameters: map[string]string{ - "database_id": "Database ID (from pganalyze_list_servers or pganalyze_get_databases)", - "limit": "Number of queries to return (default: 10)", - }, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_query_details"), - Description: "Get the full normalized query text for a specific query.", - Parameters: map[string]string{ - "database_id": "Database ID", - "query_id": "Query ID (from pganalyze_get_query_stats)", - }, - Required: []string{"database_id", "query_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_query_samples"), - Description: "Get sample executions for a query with runtime and parameters.", - Parameters: map[string]string{ - "database_id": "Database ID", - "query_id": "Query ID (from pganalyze_get_query_stats)", - }, - Required: []string{"database_id", "query_id"}, - }, - - // --- Tables --- - { - Name: mcp.ToolName("pganalyze_get_tables"), - Description: "List tables with filtering and pagination for a database.", - Parameters: map[string]string{ - "database_id": "Database ID", - "schema_name": "Filter by schema name (optional)", - "limit": "Number of tables to return (optional)", - }, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_table"), - Description: "Get detailed information about a single table: schema details, columns with per-column stats, indexes, and constraints.", - Parameters: map[string]string{ - "database_id": "Database ID", - "schema_name": "Schema name", - "table_name": "Table name", - }, - Required: []string{"database_id", "schema_name", "table_name"}, - }, - { - Name: mcp.ToolName("pganalyze_get_table_stats"), - Description: "Get time-series table statistics (row counts, dead tuples, sequential scans, etc.).", - Parameters: map[string]string{ - "database_id": "Database ID", - "schema_name": "Schema name", - "table_name": "Table name", - }, - Required: []string{"database_id", "schema_name", "table_name"}, - }, - { - Name: mcp.ToolName("pganalyze_get_index_selection"), - Description: "Get Index Advisor results for an existing run. Shows recommended indexes.", - Parameters: map[string]string{ - "database_id": "Database ID", - }, - Required: []string{"database_id"}, - }, - { - Name: mcp.ToolName("pganalyze_run_index_selection"), - Description: "Run the Index Advisor for a table to get index recommendations.", - Parameters: map[string]string{ - "database_id": "Database ID", - "schema_name": "Schema name", - "table_name": "Table name", - }, - Required: []string{"database_id", "schema_name", "table_name"}, - }, - - // --- EXPLAIN Plans --- - { - Name: mcp.ToolName("pganalyze_get_query_explains"), - Description: "List EXPLAIN plans for a query (last 7 days). Use to find query plan changes and regressions.", - Parameters: map[string]string{ - "database_id": "Database ID", - "query_id": "Query ID (from pganalyze_get_query_stats)", - }, - Required: []string{"database_id", "query_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_query_explain"), - Description: "Get a specific EXPLAIN plan with full output including node details and costs.", - Parameters: map[string]string{ - "database_id": "Database ID", - "explain_id": "EXPLAIN plan ID (from pganalyze_get_query_explains)", - }, - Required: []string{"database_id", "explain_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_query_explain_from_trace"), - Description: "Resolve an OpenTelemetry trace span to an EXPLAIN plan. Requires OpenTelemetry integration.", - Parameters: map[string]string{ - "trace_id": "OpenTelemetry trace ID", - "span_id": "OpenTelemetry span ID", - }, - Required: []string{"trace_id", "span_id"}, - }, - - // --- Backends --- - { - Name: mcp.ToolName("pganalyze_get_backend_counts"), - Description: "Get time-series connection counts by state (active, idle, waiting).", - Parameters: map[string]string{ - "server_id": "The server ID", - }, - Required: []string{"server_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_backends"), - Description: "Get a point-in-time snapshot of active connections and their states.", - Parameters: map[string]string{ - "server_id": "The server ID", - }, - Required: []string{"server_id"}, - }, - { - Name: mcp.ToolName("pganalyze_get_backend_details"), - Description: "Get details for a specific backend connection.", - Parameters: map[string]string{ - "server_id": "The server ID", - "backend_id": "The backend/connection ID", - }, - Required: []string{"server_id", "backend_id"}, - }, - - // --- Issues --- - { - Name: mcp.ToolName("pganalyze_get_issues"), - Description: "Get active check-up issues and performance alerts. Shows slow query warnings, index problems, and health issues.", - Parameters: map[string]string{ - "server_id": "Server ID to filter issues (optional)", - "database_id": "Database ID to filter issues (optional)", - }, - }, - { - Name: mcp.ToolName("pganalyze_get_checkup_status"), - Description: "Get check-up status overview for a database showing passed, warning, and critical checks.", - Parameters: map[string]string{ - "database_id": "Database ID", - }, - Required: []string{"database_id"}, - }, -} +var staticTools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/pganalyze/tools.yaml b/integrations/pganalyze/tools.yaml new file mode 100644 index 00000000..1a175813 --- /dev/null +++ b/integrations/pganalyze/tools.yaml @@ -0,0 +1,162 @@ +version: 1 +tools: + pganalyze_list_servers: + description: "List monitored PostgreSQL servers in pganalyze. Start here to discover server IDs and database IDs needed by other tools." + parameters: {} + pganalyze_get_server_details: + description: "Get details for a specific monitored PostgreSQL server including configuration and snapshot info." + parameters: + server_id: + description: "The server ID (from pganalyze_list_servers)" + required: true + pganalyze_get_postgres_settings: + description: "Get PostgreSQL configuration settings (e.g. shared_buffers, work_mem) for a server." + parameters: + server_id: + description: "The server ID (from pganalyze_list_servers)" + required: true + pganalyze_get_databases: + description: "List databases with size stats and issue counts for a server." + parameters: + server_id: + description: "The server ID (from pganalyze_list_servers)" + required: true + pganalyze_get_query_stats: + description: "Get top queries by runtime percentage. Shows expensive and slow query bottlenecks sorted by impact." + parameters: + database_id: + description: "Database ID (from pganalyze_list_servers or pganalyze_get_databases)" + required: true + limit: + description: "Number of queries to return (default: 10)" + pganalyze_get_query_details: + description: "Get the full normalized query text for a specific query." + parameters: + database_id: + description: "Database ID" + required: true + query_id: + description: "Query ID (from pganalyze_get_query_stats)" + required: true + pganalyze_get_query_samples: + description: "Get sample executions for a query with runtime and parameters." + parameters: + database_id: + description: "Database ID" + required: true + query_id: + description: "Query ID (from pganalyze_get_query_stats)" + required: true + pganalyze_get_tables: + description: "List tables with filtering and pagination for a database." + parameters: + database_id: + description: "Database ID" + required: true + schema_name: + description: "Filter by schema name (optional)" + limit: + description: "Number of tables to return (optional)" + pganalyze_get_table: + description: "Get detailed information about a single table: schema details, columns with per-column stats, indexes, and constraints." + parameters: + database_id: + description: "Database ID" + required: true + schema_name: + description: "Schema name" + required: true + table_name: + description: "Table name" + required: true + pganalyze_get_table_stats: + description: "Get time-series table statistics (row counts, dead tuples, sequential scans, etc.)." + parameters: + database_id: + description: "Database ID" + required: true + schema_name: + description: "Schema name" + required: true + table_name: + description: "Table name" + required: true + pganalyze_get_index_selection: + description: "Get Index Advisor results for an existing run. Shows recommended indexes." + parameters: + database_id: + description: "Database ID" + required: true + pganalyze_run_index_selection: + description: "Run the Index Advisor for a table to get index recommendations." + parameters: + database_id: + description: "Database ID" + required: true + schema_name: + description: "Schema name" + required: true + table_name: + description: "Table name" + required: true + pganalyze_get_query_explains: + description: "List EXPLAIN plans for a query (last 7 days). Use to find query plan changes and regressions." + parameters: + database_id: + description: "Database ID" + required: true + query_id: + description: "Query ID (from pganalyze_get_query_stats)" + required: true + pganalyze_get_query_explain: + description: "Get a specific EXPLAIN plan with full output including node details and costs." + parameters: + database_id: + description: "Database ID" + required: true + explain_id: + description: "EXPLAIN plan ID (from pganalyze_get_query_explains)" + required: true + pganalyze_get_query_explain_from_trace: + description: "Resolve an OpenTelemetry trace span to an EXPLAIN plan. Requires OpenTelemetry integration." + parameters: + trace_id: + description: "OpenTelemetry trace ID" + required: true + span_id: + description: "OpenTelemetry span ID" + required: true + pganalyze_get_backend_counts: + description: "Get time-series connection counts by state (active, idle, waiting)." + parameters: + server_id: + description: "The server ID" + required: true + pganalyze_get_backends: + description: "Get a point-in-time snapshot of active connections and their states." + parameters: + server_id: + description: "The server ID" + required: true + pganalyze_get_backend_details: + description: "Get details for a specific backend connection." + parameters: + server_id: + description: "The server ID" + required: true + backend_id: + description: "The backend/connection ID" + required: true + pganalyze_get_issues: + description: "Get active check-up issues and performance alerts. Shows slow query warnings, index problems, and health issues." + parameters: + server_id: + description: "Server ID to filter issues (optional)" + database_id: + description: "Database ID to filter issues (optional)" + pganalyze_get_checkup_status: + description: "Get check-up status overview for a database showing passed, warning, and critical checks." + parameters: + database_id: + description: "Database ID" + required: true diff --git a/integrations/postgres/postgres_test.go b/integrations/postgres/postgres_test.go index 543cacdd..584227f0 100644 --- a/integrations/postgres/postgres_test.go +++ b/integrations/postgres/postgres_test.go @@ -469,9 +469,10 @@ func TestSelectTool_InvalidIdentifier(t *testing.T) { func TestTools_RequiredFieldsAreValid(t *testing.T) { i := New() for _, tool := range i.Tools() { - for _, req := range tool.Required { - _, exists := tool.Parameters[req] - assert.True(t, exists, "tool %s: required param %s not in parameters", tool.Name, req) + // With []Parameter shape, Required is per-param — impossible state of + // required-but-undeclared is now unrepresentable. + for _, p := range tool.Parameters { + _ = p // each param carries its own Required field } } } @@ -482,8 +483,14 @@ func TestTools_AllHaveDatabaseParam(t *testing.T) { if tool.Name == "postgres_list_databases" { continue } - _, exists := tool.Parameters["database"] - assert.True(t, exists, "tool %s missing database parameter", tool.Name) + found := false + for _, p := range tool.Parameters { + if p.Name == "database" { + found = true + break + } + } + assert.True(t, found, "tool %s missing database parameter", tool.Name) } } diff --git a/integrations/postgres/tools.go b/integrations/postgres/tools.go index f3b2aa27..742cdfc1 100644 --- a/integrations/postgres/tools.go +++ b/integrations/postgres/tools.go @@ -1,243 +1,15 @@ package postgres -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -const databaseParamDesc = "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + mcp "github.com/daltoniam/switchboard" +) -var tools = []mcp.ToolDefinition{ - // --- Connection Management --- - { - Name: "postgres_list_databases", - Description: "List all configured database connections with their alias, host, database name, and read-only status", - Parameters: map[string]string{}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Schema Discovery --- - { - Name: mcp.ToolName("postgres_list_schemas"), - Description: "List all schemas in the database. Start here for schema discovery.", - Parameters: map[string]string{ - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_tables"), - Description: "List all tables in a schema with row counts and size estimates", - Parameters: map[string]string{ - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_describe_table"), - Description: "Get detailed column info for a table including types, nullability, defaults, and constraints", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("postgres_list_columns"), - Description: "List all columns for a table with data types and ordinal positions", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("postgres_list_indexes"), - Description: "List all indexes on a table with definitions and size", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("postgres_list_constraints"), - Description: "List all constraints (primary key, foreign key, unique, check) on a table", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("postgres_list_foreign_keys"), - Description: "List all foreign key relationships for a table (both referencing and referenced)", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("postgres_list_views"), - Description: "List all views in a schema with their definitions", - Parameters: map[string]string{ - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_functions"), - Description: "List user-defined functions in a schema", - Parameters: map[string]string{ - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_triggers"), - Description: "List all triggers on a table or in a schema", - Parameters: map[string]string{ - "table": "Table name (optional, lists all triggers in schema if omitted)", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_enums"), - Description: "List all enum types in the database with their values", - Parameters: map[string]string{ - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - - // --- Query Execution --- - { - Name: mcp.ToolName("postgres_query"), - Description: "Execute a read-only SQL query and return results as JSON. Use for database exploration and performance investigation. Automatically wrapped in a read-only transaction.", - Parameters: map[string]string{ - "sql": "SQL query to execute (SELECT, SHOW, EXPLAIN, etc.)", - "limit": "Max rows to return (default: 100, max: 1000)", - "database": databaseParamDesc, - }, - Required: []string{"sql"}, - }, - { - Name: mcp.ToolName("postgres_execute"), - Description: "Execute a data-modifying SQL statement (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP). Returns rows affected. **CAUTION: executes arbitrary SQL including DDL/DML. Disabled by default -- set read_only=false in credentials to enable.** DROP DATABASE and TRUNCATE are always denied.", - Parameters: map[string]string{ - "sql": "SQL statement to execute", - "database": databaseParamDesc, - }, - Required: []string{"sql"}, - }, - { - Name: mcp.ToolName("postgres_explain"), - Description: "Run EXPLAIN ANALYZE on a SQL query to show the execution plan with actual timing. Use to diagnose slow queries and optimize database performance.", - Parameters: map[string]string{ - "sql": "SQL query to explain", - "analyze": "Run EXPLAIN ANALYZE with actual execution (default: false)", - "format": "Output format: text, json, yaml, xml (default: text)", - "database": databaseParamDesc, - }, - Required: []string{"sql"}, - }, - - // --- Table Data --- - { - Name: mcp.ToolName("postgres_select"), - Description: "Select rows from a table with optional filtering, ordering, and pagination. The columns, where, and order_by parameters accept SQL expressions (semicolons and comments are rejected).", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "columns": "Comma-separated column names or SQL expressions (default: *)", - "where": "WHERE clause without the WHERE keyword (SQL expression)", - "order_by": "ORDER BY clause without the ORDER BY keyword (SQL expression)", - "limit": "Max rows to return (default: 100)", - "offset": "Number of rows to skip", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - - // --- Database Info --- - { - Name: mcp.ToolName("postgres_database_info"), - Description: "Get database-level info: version, current database, current user, server settings", - Parameters: map[string]string{ - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_database_size"), - Description: "Get the size of the current database and its largest tables", - Parameters: map[string]string{ - "limit": "Number of largest tables to return (default: 20)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_table_stats"), - Description: "Get detailed statistics for a table including row count, dead tuples, last vacuum/analyze times", - Parameters: map[string]string{ - "table": "Table name", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - Required: []string{"table"}, - }, - - // --- Roles & Permissions --- - { - Name: mcp.ToolName("postgres_list_roles"), - Description: "List all database roles with their attributes (superuser, createdb, login, etc.)", - Parameters: map[string]string{ - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_grants"), - Description: "List privileges granted on a table or schema", - Parameters: map[string]string{ - "table": "Table name (optional, shows schema-level grants if omitted)", - "schema": "Schema name (default: public)", - "database": databaseParamDesc, - }, - }, - - // --- Extensions & Connections --- - { - Name: mcp.ToolName("postgres_list_extensions"), - Description: "List all installed extensions with versions", - Parameters: map[string]string{ - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_active_connections"), - Description: "List active database connections with query state, duration, and client info", - Parameters: map[string]string{ - "state": "Filter by state: active, idle, idle in transaction (optional)", - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_list_locks"), - Description: "List current lock activity showing blocked and blocking queries", - Parameters: map[string]string{ - "database": databaseParamDesc, - }, - }, - { - Name: mcp.ToolName("postgres_running_queries"), - Description: "List currently running queries with duration and state. Use to find slow or long-running queries that may be blocking database operations.", - Parameters: map[string]string{ - "min_duration": "Minimum duration in seconds to filter by (optional)", - "database": databaseParamDesc, - }, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ // Connection Management diff --git a/integrations/postgres/tools.yaml b/integrations/postgres/tools.yaml new file mode 100644 index 00000000..b981b4c9 --- /dev/null +++ b/integrations/postgres/tools.yaml @@ -0,0 +1,207 @@ +version: 1 +tools: + postgres_list_databases: + description: "List all configured database connections with their alias, host, database name, and read-only status" + parameters: {} + postgres_list_schemas: + description: "List all schemas in the database. Start here for schema discovery." + parameters: + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_tables: + description: "List all tables in a schema with row counts and size estimates" + parameters: + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_describe_table: + description: "Get detailed column info for a table including types, nullability, defaults, and constraints" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_columns: + description: "List all columns for a table with data types and ordinal positions" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_indexes: + description: "List all indexes on a table with definitions and size" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_constraints: + description: "List all constraints (primary key, foreign key, unique, check) on a table" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_foreign_keys: + description: "List all foreign key relationships for a table (both referencing and referenced)" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_views: + description: "List all views in a schema with their definitions" + parameters: + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_functions: + description: "List user-defined functions in a schema" + parameters: + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_triggers: + description: "List all triggers on a table or in a schema" + parameters: + table: + description: "Table name (optional, lists all triggers in schema if omitted)" + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_enums: + description: "List all enum types in the database with their values" + parameters: + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_query: + description: "Execute a read-only SQL query and return results as JSON. Use for database exploration and performance investigation. Automatically wrapped in a read-only transaction." + parameters: + sql: + description: "SQL query to execute (SELECT, SHOW, EXPLAIN, etc.)" + required: true + limit: + description: "Max rows to return (default: 100, max: 1000)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_execute: + description: "Execute a data-modifying SQL statement (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP). Returns rows affected. **CAUTION: executes arbitrary SQL including DDL/DML. Disabled by default -- set read_only=false in credentials to enable.** DROP DATABASE and TRUNCATE are always denied." + parameters: + sql: + description: "SQL statement to execute" + required: true + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_explain: + description: "Run EXPLAIN ANALYZE on a SQL query to show the execution plan with actual timing. Use to diagnose slow queries and optimize database performance." + parameters: + sql: + description: "SQL query to explain" + required: true + analyze: + description: "Run EXPLAIN ANALYZE with actual execution (default: false)" + format: + description: "Output format: text, json, yaml, xml (default: text)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_select: + description: "Select rows from a table with optional filtering, ordering, and pagination. The columns, where, and order_by parameters accept SQL expressions (semicolons and comments are rejected)." + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + columns: + description: "Comma-separated column names or SQL expressions (default: *)" + where: + description: "WHERE clause without the WHERE keyword (SQL expression)" + order_by: + description: "ORDER BY clause without the ORDER BY keyword (SQL expression)" + limit: + description: "Max rows to return (default: 100)" + offset: + description: "Number of rows to skip" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_database_info: + description: "Get database-level info: version, current database, current user, server settings" + parameters: + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_database_size: + description: "Get the size of the current database and its largest tables" + parameters: + limit: + description: "Number of largest tables to return (default: 20)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_table_stats: + description: "Get detailed statistics for a table including row count, dead tuples, last vacuum/analyze times" + parameters: + table: + description: "Table name" + required: true + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_roles: + description: "List all database roles with their attributes (superuser, createdb, login, etc.)" + parameters: + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_grants: + description: "List privileges granted on a table or schema" + parameters: + table: + description: "Table name (optional, shows schema-level grants if omitted)" + schema: + description: "Schema name (default: public)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_extensions: + description: "List all installed extensions with versions" + parameters: + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_active_connections: + description: "List active database connections with query state, duration, and client info" + parameters: + state: + description: "Filter by state: active, idle, idle in transaction (optional)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_list_locks: + description: "List current lock activity showing blocked and blocking queries" + parameters: + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + postgres_running_queries: + description: "List currently running queries with duration and state. Use to find slow or long-running queries that may be blocking database operations." + parameters: + min_duration: + description: "Minimum duration in seconds to filter by (optional)" + database: + description: "Connection alias (omit to use default). Use postgres_list_databases to see available connections." diff --git a/integrations/posthog/tools.go b/integrations/posthog/tools.go index 670d6f57..0641ed34 100644 --- a/integrations/posthog/tools.go +++ b/integrations/posthog/tools.go @@ -1,299 +1,12 @@ package posthog -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Projects ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_projects"), Description: "List all projects in the PostHog organization. Start here to discover projects.", - Parameters: map[string]string{"limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_project"), Description: "Get details of a specific project", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)"}, - }, - { - Name: mcp.ToolName("posthog_create_project"), Description: "Create a new project", - Parameters: map[string]string{"name": "Project name"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("posthog_update_project"), Description: "Update a project's settings", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "New name"}, - }, - { - Name: mcp.ToolName("posthog_delete_project"), Description: "Delete a project", - Parameters: map[string]string{"project_id": "Project ID"}, - Required: []string{"project_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Feature Flags ─────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_feature_flags"), Description: "List feature flags for rollout targeting and release management. Filter by active state, type, or experiment.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "search": "Search by key or name", "active": "Filter by active state (true/false)", "type": "Filter by type: boolean, multivariant, experiment", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_feature_flag"), Description: "Get details of a specific feature flag", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "flag_id": "Feature flag ID"}, - Required: []string{"flag_id"}, - }, - { - Name: mcp.ToolName("posthog_create_feature_flag"), Description: "Create a new feature flag", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "key": "Feature flag key (unique identifier)", "name": "Human-readable name", "filters": "JSON string of filter/rollout config", "active": "Whether flag is active (true/false)", "ensure_experience_continuity": "Persist flag value for users across sessions (true/false)"}, - Required: []string{"key"}, - }, - { - Name: mcp.ToolName("posthog_update_feature_flag"), Description: "Update a feature flag", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "flag_id": "Feature flag ID", "key": "Feature flag key", "name": "Human-readable name", "filters": "JSON string of filter/rollout config", "active": "Whether flag is active (true/false)"}, - Required: []string{"flag_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_feature_flag"), Description: "Delete a feature flag (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "flag_id": "Feature flag ID"}, - Required: []string{"flag_id"}, - }, - { - Name: mcp.ToolName("posthog_feature_flag_activity"), Description: "Get activity log for a feature flag", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "flag_id": "Feature flag ID", "limit": "Max results", "offset": "Pagination offset"}, - Required: []string{"flag_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Cohorts ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_cohorts"), Description: "List all user cohorts for audience segmentation and analytics targeting", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_cohort"), Description: "Get details of a specific cohort", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "cohort_id": "Cohort ID"}, - Required: []string{"cohort_id"}, - }, - { - Name: mcp.ToolName("posthog_create_cohort"), Description: "Create a new cohort", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Cohort name", "description": "Cohort description", "filters": "JSON string of cohort filters", "is_static": "Whether cohort is static (true/false)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("posthog_update_cohort"), Description: "Update a cohort", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "cohort_id": "Cohort ID", "name": "Cohort name", "description": "Cohort description", "filters": "JSON string of cohort filters"}, - Required: []string{"cohort_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_cohort"), Description: "Delete a cohort (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "cohort_id": "Cohort ID"}, - Required: []string{"cohort_id"}, - }, - { - Name: mcp.ToolName("posthog_list_cohort_persons"), Description: "List persons in a cohort", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "cohort_id": "Cohort ID", "limit": "Max results", "offset": "Pagination offset"}, - Required: []string{"cohort_id"}, - }, - - // ── Insights ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_insights"), Description: "List saved product analytics insights (trends, funnels, retention, etc.). View reports, charts, and metrics.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "search": "Search by name", "limit": "Max results", "offset": "Pagination offset", "created_by": "Filter by creator user ID"}, - }, - { - Name: mcp.ToolName("posthog_get_insight"), Description: "Get details of a specific product analytics insight, including chart data for trends, funnels, and retention", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "insight_id": "Insight ID"}, - Required: []string{"insight_id"}, - }, - { - Name: mcp.ToolName("posthog_create_insight"), Description: "Create a new insight", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Insight name", "description": "Insight description", "filters": "JSON string of insight filters (events, actions, properties, date ranges)", "query": "JSON string of HogQL query definition"}, - }, - { - Name: mcp.ToolName("posthog_update_insight"), Description: "Update an insight", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "insight_id": "Insight ID", "name": "Insight name", "description": "Insight description", "filters": "JSON string of insight filters", "query": "JSON string of HogQL query definition"}, - Required: []string{"insight_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_insight"), Description: "Delete an insight (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "insight_id": "Insight ID"}, - Required: []string{"insight_id"}, - }, - { - Name: mcp.ToolName("posthog_query"), Description: "Run a HogQL (SQL-like) query synchronously and return inline results. Use this for ad-hoc analytics without persisting an insight. Returns columns, results rows, and execution metadata.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "query": "HogQL query text, e.g. SELECT count() FROM events WHERE timestamp > now() - INTERVAL 7 DAY", "client_query_id": "Optional client-supplied identifier to correlate the request in PostHog logs", "refresh": "Optional cache mode: 'blocking' (default-ish, wait for fresh result), 'force_blocking', 'lazy_async', 'force_cache'"}, - Required: []string{"query"}, - }, - - // ── Persons ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_persons"), Description: "List persons (users and customers) tracked by PostHog analytics. Search by email, distinct ID, or visitor profile.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "search": "Search by email or distinct ID", "distinct_id": "Filter by exact distinct ID", "email": "Filter by email", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_person"), Description: "Get details of a specific person", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "person_id": "Person UUID"}, - Required: []string{"person_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_person"), Description: "Delete a person and their data", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "person_id": "Person UUID"}, - Required: []string{"person_id"}, - }, - { - Name: mcp.ToolName("posthog_update_person_property"), Description: "Update a property on a person", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "person_id": "Person UUID", "key": "Property key", "value": "Property value"}, - Required: []string{"person_id", "key", "value"}, - }, - { - Name: mcp.ToolName("posthog_delete_person_property"), Description: "Delete a property from a person", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "person_id": "Person UUID", "key": "Property key to remove"}, - Required: []string{"person_id", "key"}, - }, - - // ── Groups ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_groups"), Description: "List groups", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "group_type_index": "Group type index (0-based)", "search": "Search by group key or properties", "cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("posthog_find_group"), Description: "Find a specific group by key", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "group_type_index": "Group type index (0-based)", "group_key": "Group key to find"}, - Required: []string{"group_type_index", "group_key"}, - }, - - // ── Annotations ───────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_annotations"), Description: "List annotations (markers on charts)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "search": "Search by content", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_annotation"), Description: "Get details of a specific annotation", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "annotation_id": "Annotation ID"}, - Required: []string{"annotation_id"}, - }, - { - Name: mcp.ToolName("posthog_create_annotation"), Description: "Create a new annotation", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "content": "Annotation text", "date_marker": "ISO 8601 date for the annotation marker", "scope": "Scope: dashboard_item, project, organization"}, - Required: []string{"content", "date_marker"}, - }, - { - Name: mcp.ToolName("posthog_update_annotation"), Description: "Update an annotation", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "annotation_id": "Annotation ID", "content": "Annotation text", "date_marker": "ISO 8601 date", "scope": "Scope: dashboard_item, project, organization"}, - Required: []string{"annotation_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_annotation"), Description: "Delete an annotation", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "annotation_id": "Annotation ID"}, - Required: []string{"annotation_id"}, - }, - - // ── Dashboards ────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_dashboards"), Description: "List PostHog product analytics dashboards for metrics overview and monitoring", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_dashboard"), Description: "Get details of a specific dashboard", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "dashboard_id": "Dashboard ID"}, - Required: []string{"dashboard_id"}, - }, - { - Name: mcp.ToolName("posthog_create_dashboard"), Description: "Create a new dashboard", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Dashboard name", "description": "Dashboard description", "pinned": "Pin dashboard (true/false)", "tags": "Comma-separated tags"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("posthog_update_dashboard"), Description: "Update a dashboard", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "dashboard_id": "Dashboard ID", "name": "Dashboard name", "description": "Dashboard description", "pinned": "Pin dashboard (true/false)", "tags": "Comma-separated tags"}, - Required: []string{"dashboard_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_dashboard"), Description: "Delete a dashboard (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "dashboard_id": "Dashboard ID"}, - Required: []string{"dashboard_id"}, - }, - - // ── Actions ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_actions"), Description: "List actions (custom event groupings) for analytics tracking and conversion metrics", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_action"), Description: "Get details of a specific action", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "action_id": "Action ID"}, - Required: []string{"action_id"}, - }, - { - Name: mcp.ToolName("posthog_create_action"), Description: "Create a new action", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Action name", "description": "Action description", "steps": "JSON array of action step definitions", "tags": "Comma-separated tags"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("posthog_update_action"), Description: "Update an action", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "action_id": "Action ID", "name": "Action name", "description": "Action description", "steps": "JSON array of action step definitions", "tags": "Comma-separated tags"}, - Required: []string{"action_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_action"), Description: "Delete an action (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "action_id": "Action ID"}, - Required: []string{"action_id"}, - }, - - // ── Events ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_events"), Description: "List captured product analytics events. View user behavior tracking and activity data.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "event": "Filter by event name", "person_id": "Filter by person UUID", "distinct_id": "Filter by distinct ID", "properties": "JSON string of property filters", "before": "ISO 8601 timestamp upper bound", "after": "ISO 8601 timestamp lower bound", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_event"), Description: "Get details of a specific event", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "event_id": "Event UUID"}, - Required: []string{"event_id"}, - }, - - // ── Experiments ───────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_experiments"), Description: "List A/B test experiments for conversion optimization. View variant results and analytics.", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_experiment"), Description: "Get details of a specific experiment", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "experiment_id": "Experiment ID"}, - Required: []string{"experiment_id"}, - }, - { - Name: mcp.ToolName("posthog_create_experiment"), Description: "Create a new experiment", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Experiment name", "description": "Experiment description", "feature_flag_key": "Feature flag key to use for experiment", "start_date": "ISO 8601 start date", "end_date": "ISO 8601 end date", "filters": "JSON string of experiment filters"}, - Required: []string{"name", "feature_flag_key"}, - }, - { - Name: mcp.ToolName("posthog_update_experiment"), Description: "Update an experiment", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "experiment_id": "Experiment ID", "name": "Experiment name", "description": "Experiment description", "start_date": "ISO 8601 start date", "end_date": "ISO 8601 end date"}, - Required: []string{"experiment_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_experiment"), Description: "Delete an experiment (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "experiment_id": "Experiment ID"}, - Required: []string{"experiment_id"}, - }, - - // ── Surveys ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("posthog_list_surveys"), Description: "List surveys", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "limit": "Max results", "offset": "Pagination offset"}, - }, - { - Name: mcp.ToolName("posthog_get_survey"), Description: "Get details of a specific survey", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "survey_id": "Survey ID"}, - Required: []string{"survey_id"}, - }, - { - Name: mcp.ToolName("posthog_create_survey"), Description: "Create a new survey", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "name": "Survey name", "description": "Survey description", "type": "Survey type", "questions": "JSON array of question definitions", "targeting_flag_filters": "JSON string of targeting filters"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("posthog_update_survey"), Description: "Update a survey", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "survey_id": "Survey ID", "name": "Survey name", "description": "Survey description", "questions": "JSON array of question definitions"}, - Required: []string{"survey_id"}, - }, - { - Name: mcp.ToolName("posthog_delete_survey"), Description: "Delete a survey (soft delete)", - Parameters: map[string]string{"project_id": "Project ID (uses default if configured, otherwise required)", "survey_id": "Survey ID"}, - Required: []string{"survey_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/posthog/tools.yaml b/integrations/posthog/tools.yaml new file mode 100644 index 00000000..b06f3d51 --- /dev/null +++ b/integrations/posthog/tools.yaml @@ -0,0 +1,685 @@ +version: 1 +tools: + posthog_list_projects: + description: "List all projects in the PostHog organization. Start here to discover projects." + parameters: + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_project: + description: "Get details of a specific project" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + + posthog_create_project: + description: "Create a new project" + parameters: + name: + description: "Project name" + required: true + + posthog_update_project: + description: "Update a project's settings" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "New name" + + posthog_delete_project: + description: "Delete a project" + parameters: + project_id: + description: "Project ID" + required: true + + posthog_list_feature_flags: + description: "List feature flags for rollout targeting and release management. Filter by active state, type, or experiment." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + search: + description: "Search by key or name" + active: + description: "Filter by active state (true/false)" + type: + description: "Filter by type: boolean, multivariant, experiment" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_feature_flag: + description: "Get details of a specific feature flag" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + flag_id: + description: "Feature flag ID" + required: true + + posthog_create_feature_flag: + description: "Create a new feature flag" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + key: + description: "Feature flag key (unique identifier)" + required: true + name: + description: "Human-readable name" + filters: + description: "JSON string of filter/rollout config" + active: + description: "Whether flag is active (true/false)" + ensure_experience_continuity: + description: "Persist flag value for users across sessions (true/false)" + + posthog_update_feature_flag: + description: "Update a feature flag" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + flag_id: + description: "Feature flag ID" + required: true + key: + description: "Feature flag key" + name: + description: "Human-readable name" + filters: + description: "JSON string of filter/rollout config" + active: + description: "Whether flag is active (true/false)" + + posthog_delete_feature_flag: + description: "Delete a feature flag (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + flag_id: + description: "Feature flag ID" + required: true + + posthog_feature_flag_activity: + description: "Get activity log for a feature flag" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + flag_id: + description: "Feature flag ID" + required: true + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_list_cohorts: + description: "List all user cohorts for audience segmentation and analytics targeting" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_cohort: + description: "Get details of a specific cohort" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + cohort_id: + description: "Cohort ID" + required: true + + posthog_create_cohort: + description: "Create a new cohort" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Cohort name" + required: true + description: + description: "Cohort description" + filters: + description: "JSON string of cohort filters" + is_static: + description: "Whether cohort is static (true/false)" + + posthog_update_cohort: + description: "Update a cohort" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + cohort_id: + description: "Cohort ID" + required: true + name: + description: "Cohort name" + description: + description: "Cohort description" + filters: + description: "JSON string of cohort filters" + + posthog_delete_cohort: + description: "Delete a cohort (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + cohort_id: + description: "Cohort ID" + required: true + + posthog_list_cohort_persons: + description: "List persons in a cohort" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + cohort_id: + description: "Cohort ID" + required: true + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_list_insights: + description: "List saved product analytics insights (trends, funnels, retention, etc.). View reports, charts, and metrics." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + search: + description: "Search by name" + limit: + description: "Max results" + offset: + description: "Pagination offset" + created_by: + description: "Filter by creator user ID" + + posthog_get_insight: + description: "Get details of a specific product analytics insight, including chart data for trends, funnels, and retention" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + insight_id: + description: "Insight ID" + required: true + + posthog_create_insight: + description: "Create a new insight" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Insight name" + description: + description: "Insight description" + filters: + description: "JSON string of insight filters (events, actions, properties, date ranges)" + query: + description: "JSON string of HogQL query definition" + + posthog_update_insight: + description: "Update an insight" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + insight_id: + description: "Insight ID" + required: true + name: + description: "Insight name" + description: + description: "Insight description" + filters: + description: "JSON string of insight filters" + query: + description: "JSON string of HogQL query definition" + + posthog_delete_insight: + description: "Delete an insight (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + insight_id: + description: "Insight ID" + required: true + + posthog_query: + description: "Run a HogQL (SQL-like) query synchronously and return inline results. Use this for ad-hoc analytics without persisting an insight. Returns columns, results rows, and execution metadata." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + query: + description: "HogQL query text, e.g. SELECT count() FROM events WHERE timestamp > now() - INTERVAL 7 DAY" + required: true + client_query_id: + description: "Optional client-supplied identifier to correlate the request in PostHog logs" + refresh: + description: "Optional cache mode: 'blocking' (default-ish, wait for fresh result), 'force_blocking', 'lazy_async', 'force_cache'" + + posthog_list_persons: + description: "List persons (users and customers) tracked by PostHog analytics. Search by email, distinct ID, or visitor profile." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + search: + description: "Search by email or distinct ID" + distinct_id: + description: "Filter by exact distinct ID" + email: + description: "Filter by email" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_person: + description: "Get details of a specific person" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + person_id: + description: "Person UUID" + required: true + + posthog_delete_person: + description: "Delete a person and their data" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + person_id: + description: "Person UUID" + required: true + + posthog_update_person_property: + description: "Update a property on a person" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + person_id: + description: "Person UUID" + required: true + key: + description: "Property key" + required: true + value: + description: "Property value" + required: true + + posthog_delete_person_property: + description: "Delete a property from a person" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + person_id: + description: "Person UUID" + required: true + key: + description: "Property key to remove" + required: true + + posthog_list_groups: + description: "List groups" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + group_type_index: + description: "Group type index (0-based)" + search: + description: "Search by group key or properties" + cursor: + description: "Pagination cursor" + + posthog_find_group: + description: "Find a specific group by key" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + group_type_index: + description: "Group type index (0-based)" + required: true + group_key: + description: "Group key to find" + required: true + + posthog_list_annotations: + description: "List annotations (markers on charts)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + search: + description: "Search by content" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_annotation: + description: "Get details of a specific annotation" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + annotation_id: + description: "Annotation ID" + required: true + + posthog_create_annotation: + description: "Create a new annotation" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + content: + description: "Annotation text" + required: true + date_marker: + description: "ISO 8601 date for the annotation marker" + required: true + scope: + description: "Scope: dashboard_item, project, organization" + + posthog_update_annotation: + description: "Update an annotation" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + annotation_id: + description: "Annotation ID" + required: true + content: + description: "Annotation text" + date_marker: + description: "ISO 8601 date" + scope: + description: "Scope: dashboard_item, project, organization" + + posthog_delete_annotation: + description: "Delete an annotation" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + annotation_id: + description: "Annotation ID" + required: true + + posthog_list_dashboards: + description: "List PostHog product analytics dashboards for metrics overview and monitoring" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_dashboard: + description: "Get details of a specific dashboard" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + dashboard_id: + description: "Dashboard ID" + required: true + + posthog_create_dashboard: + description: "Create a new dashboard" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Dashboard name" + required: true + description: + description: "Dashboard description" + pinned: + description: "Pin dashboard (true/false)" + tags: + description: "Comma-separated tags" + + posthog_update_dashboard: + description: "Update a dashboard" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + dashboard_id: + description: "Dashboard ID" + required: true + name: + description: "Dashboard name" + description: + description: "Dashboard description" + pinned: + description: "Pin dashboard (true/false)" + tags: + description: "Comma-separated tags" + + posthog_delete_dashboard: + description: "Delete a dashboard (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + dashboard_id: + description: "Dashboard ID" + required: true + + posthog_list_actions: + description: "List actions (custom event groupings) for analytics tracking and conversion metrics" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_action: + description: "Get details of a specific action" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + action_id: + description: "Action ID" + required: true + + posthog_create_action: + description: "Create a new action" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Action name" + required: true + description: + description: "Action description" + steps: + description: "JSON array of action step definitions" + tags: + description: "Comma-separated tags" + + posthog_update_action: + description: "Update an action" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + action_id: + description: "Action ID" + required: true + name: + description: "Action name" + description: + description: "Action description" + steps: + description: "JSON array of action step definitions" + tags: + description: "Comma-separated tags" + + posthog_delete_action: + description: "Delete an action (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + action_id: + description: "Action ID" + required: true + + posthog_list_events: + description: "List captured product analytics events. View user behavior tracking and activity data." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + event: + description: "Filter by event name" + person_id: + description: "Filter by person UUID" + distinct_id: + description: "Filter by distinct ID" + properties: + description: "JSON string of property filters" + before: + description: "ISO 8601 timestamp upper bound" + after: + description: "ISO 8601 timestamp lower bound" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_event: + description: "Get details of a specific event" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + event_id: + description: "Event UUID" + required: true + + posthog_list_experiments: + description: "List A/B test experiments for conversion optimization. View variant results and analytics." + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_experiment: + description: "Get details of a specific experiment" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + experiment_id: + description: "Experiment ID" + required: true + + posthog_create_experiment: + description: "Create a new experiment" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Experiment name" + required: true + description: + description: "Experiment description" + feature_flag_key: + description: "Feature flag key to use for experiment" + required: true + start_date: + description: "ISO 8601 start date" + end_date: + description: "ISO 8601 end date" + filters: + description: "JSON string of experiment filters" + + posthog_update_experiment: + description: "Update an experiment" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + experiment_id: + description: "Experiment ID" + required: true + name: + description: "Experiment name" + description: + description: "Experiment description" + start_date: + description: "ISO 8601 start date" + end_date: + description: "ISO 8601 end date" + + posthog_delete_experiment: + description: "Delete an experiment (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + experiment_id: + description: "Experiment ID" + required: true + + posthog_list_surveys: + description: "List surveys" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + limit: + description: "Max results" + offset: + description: "Pagination offset" + + posthog_get_survey: + description: "Get details of a specific survey" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + survey_id: + description: "Survey ID" + required: true + + posthog_create_survey: + description: "Create a new survey" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + name: + description: "Survey name" + required: true + description: + description: "Survey description" + type: + description: "Survey type" + questions: + description: "JSON array of question definitions" + targeting_flag_filters: + description: "JSON string of targeting filters" + + posthog_update_survey: + description: "Update a survey" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + survey_id: + description: "Survey ID" + required: true + name: + description: "Survey name" + description: + description: "Survey description" + questions: + description: "JSON array of question definitions" + + posthog_delete_survey: + description: "Delete a survey (soft delete)" + parameters: + project_id: + description: "Project ID (uses default if configured, otherwise required)" + survey_id: + description: "Survey ID" + required: true diff --git a/integrations/rwx/proxy.go b/integrations/rwx/proxy.go index c2ff396a..f42f627a 100644 --- a/integrations/rwx/proxy.go +++ b/integrations/rwx/proxy.go @@ -227,16 +227,17 @@ func (p *proxyClient) fetchTools() error { func (p *proxyClient) toolDefinitions() []mcp.ToolDefinition { var defs []mcp.ToolDefinition for _, t := range p.tools { - params := make(map[string]string) - if props, ok := t.InputSchema["properties"].(map[string]interface{}); ok { - required := make(map[string]bool) - if reqList, ok := t.InputSchema["required"].([]interface{}); ok { - for _, r := range reqList { - if s, ok := r.(string); ok { - required[s] = true - } + required := make(map[string]bool) + if reqList, ok := t.InputSchema["required"].([]interface{}); ok { + for _, r := range reqList { + if s, ok := r.(string); ok { + required[s] = true } } + } + + var params []mcp.Parameter + if props, ok := t.InputSchema["properties"].(map[string]interface{}); ok { for key, val := range props { desc := "" if propMap, ok := val.(map[string]interface{}); ok { @@ -244,28 +245,23 @@ func (p *proxyClient) toolDefinitions() []mcp.ToolDefinition { desc = transformCLIReferences(d) } } - params[key] = desc + params = append(params, mcp.Parameter{ + Name: mcp.ParamName(key), + Description: desc, + Required: required[key], + }) } } + desc := t.Description if desc != "" { desc = transformCLIReferences(desc) } - var requiredFields []string - if reqList, ok := t.InputSchema["required"].([]interface{}); ok { - for _, r := range reqList { - if s, ok := r.(string); ok { - requiredFields = append(requiredFields, s) - } - } - } - defs = append(defs, mcp.ToolDefinition{ Name: mcp.ToolName("rwx_proxy_" + t.Name), Description: "[Proxied from rwx mcp serve] " + desc, Parameters: params, - Required: requiredFields, }) } return defs diff --git a/integrations/rwx/rwx_test.go b/integrations/rwx/rwx_test.go index 7b0ba674..8efddd89 100644 --- a/integrations/rwx/rwx_test.go +++ b/integrations/rwx/rwx_test.go @@ -323,8 +323,15 @@ func TestProxyToolDefinitions_TransformsCLIReferences(t *testing.T) { require.Len(t, defs, 1) assert.Equal(t, mcp.ToolName("rwx_proxy_some_tool"), defs[0].Name) assert.Contains(t, defs[0].Description, "rwx_get_task_logs") - assert.Contains(t, defs[0].Parameters["id"], "rwx_get_run_results") - assert.Equal(t, []string{"id"}, defs[0].Required) + found := false + for _, p := range defs[0].Parameters { + if string(p.Name) == "id" { + assert.Contains(t, p.Description, "rwx_get_run_results") + assert.True(t, p.Required) + found = true + } + } + assert.True(t, found, "id param not found") } // --- JSON marshal test --- diff --git a/integrations/rwx/tools.go b/integrations/rwx/tools.go index 360f0f10..51defdaf 100644 --- a/integrations/rwx/tools.go +++ b/integrations/rwx/tools.go @@ -1,204 +1,12 @@ package rwx -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Runs ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_launch_ci_run"), - Description: "CLI-backed: launches a CI/CD pipeline run by shelling out to the local rwx CLI. Start here to run CI. Executes the specified workflow file (default: .rwx/ci.yml).", - Parameters: map[string]string{ - "workflow": "Path to the RWX workflow YAML file to run (default: .rwx/ci.yml, e.g. .rwx/auto-deploy.yml)", - "targets": "JSON array of specific task keys to target (optional)", - "wait": "Wait for the run to complete before returning (true/false, default: false)", - "title": "Display title for the run in the RWX UI (optional)", - "init": "JSON object of init parameters available in the init context (optional, e.g. {\"deploy_env\": \"staging\"})", - }, - }, - { - Name: mcp.ToolName("rwx_dispatch_run"), - Description: "CLI-backed: launches a run by shelling out to the local rwx CLI for pre-configured RWX dispatch workflows. Use instead of rwx_launch_ci_run when triggering remote/pre-configured workflows without local files.", - Parameters: map[string]string{ - "dispatch_key": "The dispatch key identifying the pre-configured workflow", - "ref": "Git ref (branch/tag/SHA) to use for the run (optional)", - "params": "JSON object of dispatch params available in event.dispatch.params context (optional)", - "wait": "Wait for the run to complete before returning (true/false, default: false)", - "title": "Display title for the run in the RWX UI (optional)", - }, - Required: []string{"dispatch_key"}, - }, - { - Name: mcp.ToolName("rwx_wait_for_ci_run"), - Description: "Poll and wait for an RWX CI run to complete or timeout", - Parameters: map[string]string{ - "run_id": "RWX run ID or full URL to wait for", - "timeout_seconds": "Maximum time to wait in seconds (default: 1800)", - "poll_interval_seconds": "Seconds between status checks (default: 30)", - }, - Required: []string{"run_id"}, - }, - { - Name: mcp.ToolName("rwx_get_recent_runs"), - Description: "Get recent CI/CD runs for a git branch. Returns all workflow runs by default; use definition_path to filter to a specific workflow.", - Parameters: map[string]string{ - "ref": "Git ref (branch name) to filter runs by", - "limit": "Number of runs to return (default: 5)", - "definition_path": "Filter to a specific workflow file (e.g. .rwx/ci.yml, .rwx/auto-deploy.yml). Omit to return all workflows.", - }, - Required: []string{"ref"}, - }, - { - Name: mcp.ToolName("rwx_get_run_results"), - Description: "Get structured CI/CD pipeline results directly from the RWX API, including pass/fail status, failed tests, and build errors. Accepts a run ID/full URL or branch/commit lookup.", - Parameters: map[string]string{ - "run_id": "RWX run ID or full URL (required unless branch is provided)", - "task_key": "Get results for a specific task by key (e.g. ci.checks.lint) instead of the full run", - "branch": "Look up results by branch name instead of run ID (uses current repo unless repo is set)", - "commit": "Look up results by commit SHA instead of run ID", - "repo": "Repository name for branch/commit lookup (default: current git repo)", - "definition": "Definition path for branch/commit lookup (e.g. .rwx/ci.yml)", - }, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Logs ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_get_task_logs"), - Description: "Download and return full CI/CD task logs directly from the RWX API with build failure and test failure highlights", - Parameters: map[string]string{ - "task_id": "RWX task ID (32-char hex) or task URL", - "run_id": "RWX run ID — use with task_key to resolve by key instead of ID (optional)", - "task_key": "Task key (e.g. ci.checks.lint) — requires run_id. Use instead of task_id.", - }, - }, - { - Name: mcp.ToolName("rwx_head_logs"), - Description: "Return the first N lines of logs for a run or task directly from the RWX API. Supports pagination via offset.", - Parameters: map[string]string{ - "id": "RWX run ID or task ID", - "lines": "Number of lines to return from the beginning (default: 50, max: 50)", - "offset": "Line offset to start from (default: 0). Use for pagination.", - "task_key": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("rwx_tail_logs"), - Description: "Return the last N lines of logs for a run or task directly from the RWX API. Supports pagination via offset.", - Parameters: map[string]string{ - "id": "RWX run ID or task ID", - "lines": "Number of lines to return from the end (default: 50, max: 50)", - "offset": "Line offset from the end (default: 0). Use for pagination to see earlier lines.", - "task_key": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("rwx_grep_logs"), - Description: "Search CI/CD build and test logs fetched directly from the RWX API for a pattern with context lines. Results are paginated (50 lines per page).", - Parameters: map[string]string{ - "id": "RWX run ID or task ID", - "pattern": "Pattern to search for in the logs (case-insensitive)", - "context": "Number of context lines before and after matches (default: 3)", - "page": "Page number (default: 1). Each page returns up to 50 lines of output.", - "task_key": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key", - }, - Required: []string{"id", "pattern"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Artifacts ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_get_artifacts"), - Description: "List or download artifacts for a run directly from the RWX API. Listed results omit download tokens; download=true fetches artifact content.", - Parameters: map[string]string{ - "run_id": "RWX run ID or full URL to get artifacts for", - "download": "Download artifacts (true/false, default: false — just list)", - "artifact_key": "Specific artifact key to download (optional, downloads all if not specified)", - "task_id": "RWX task ID or task URL to list/download artifacts for a specific task (optional)", - "task_key": "Task key to list/download artifacts for a task within run_id (optional)", - }, - }, - - // ── Workflow ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_validate_workflow"), - Description: "CLI-backed: validate an RWX workflow YAML file by shelling out to the local rwx CLI.", - Parameters: map[string]string{ - "file_path": "Path to the RWX workflow YAML file to validate (default: .rwx/ci.yml)", - }, - }, - - // ── Docs ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_docs_search"), - Description: "CLI-backed: search RWX documentation by shelling out to the local rwx CLI. Use to find docs on caching, parallelism, configuration, and other RWX features.", - Parameters: map[string]string{ - "query": "Search query (e.g. 'caching', 'parallelism', 'filtering files')", - "limit": "Maximum number of results (default: 5)", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("rwx_docs_pull"), - Description: "CLI-backed: fetch an RWX documentation article as markdown by shelling out to the local rwx CLI. Use a URL from rwx_docs_search results or a docs path like /docs/caching.", - Parameters: map[string]string{ - "url_or_path": "Full URL (https://www.rwx.com/docs/...) or path (/docs/caching) of the article to fetch", - }, - Required: []string{"url_or_path"}, - }, - - // ── Vaults ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_vaults_var_show"), - Description: "CLI-backed: show a variable value from an RWX vault by shelling out to the local rwx CLI.", - Parameters: map[string]string{ - "name": "Variable name to show", - "vault": "Vault name (default: 'default')", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("rwx_vaults_var_set"), - Description: "CLI-backed: set a variable in an RWX vault by shelling out to the local rwx CLI.", - Parameters: map[string]string{ - "name": "Variable name", - "value": "Variable value", - "vault": "Vault name (default: 'default')", - }, - Required: []string{"name", "value"}, - }, - { - Name: mcp.ToolName("rwx_vaults_var_delete"), - Description: "CLI-backed: delete a variable from an RWX vault by shelling out to the local rwx CLI.", - Parameters: map[string]string{ - "name": "Variable name to delete", - "vault": "Vault name (default: 'default')", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("rwx_vaults_secret_set"), - Description: "CLI-backed: set a secret in an RWX vault by shelling out to the local rwx CLI. The value is stored encrypted and cannot be read back.", - Parameters: map[string]string{ - "name": "Secret name", - "value": "Secret value", - "vault": "Vault name (default: 'default')", - }, - Required: []string{"name", "value"}, - }, - { - Name: mcp.ToolName("rwx_vaults_secret_delete"), - Description: "CLI-backed: delete a secret from an RWX vault by shelling out to the local rwx CLI.", - Parameters: map[string]string{ - "name": "Secret name to delete", - "vault": "Vault name (default: 'default')", - }, - Required: []string{"name"}, - }, - - // ── CLI ───────────────────────────────────────────────────────── - { - Name: mcp.ToolName("rwx_verify_cli"), - Description: "CLI-backed: verify the rwx CLI is installed and meets the minimum version requirement (>= " + minRWXVersion + ")", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/rwx/tools.yaml b/integrations/rwx/tools.yaml new file mode 100644 index 00000000..5a613a55 --- /dev/null +++ b/integrations/rwx/tools.yaml @@ -0,0 +1,190 @@ +version: 1 +tools: + rwx_launch_ci_run: + description: "Launch a CI/CD pipeline run using the rwx CLI. Start here to run CI. Executes the specified workflow file (default: .rwx/ci.yml)." + parameters: + workflow: + description: "Path to the RWX workflow YAML file to run (default: .rwx/ci.yml, e.g. .rwx/auto-deploy.yml)" + targets: + description: "JSON array of specific task keys to target (optional)" + wait: + description: "Wait for the run to complete before returning (true/false, default: false)" + title: + description: "Display title for the run in the RWX UI (optional)" + init: + description: 'JSON object of init parameters available in the init context (optional, e.g. {"deploy_env": "staging"})' + rwx_dispatch_run: + description: "Launch a run from a pre-configured RWX dispatch workflow by key. Use instead of rwx_launch_ci_run when triggering remote/pre-configured workflows without local files." + parameters: + dispatch_key: + description: "The dispatch key identifying the pre-configured workflow" + required: true + ref: + description: "Git ref (branch/tag/SHA) to use for the run (optional)" + params: + description: "JSON object of dispatch params available in event.dispatch.params context (optional)" + wait: + description: "Wait for the run to complete before returning (true/false, default: false)" + title: + description: "Display title for the run in the RWX UI (optional)" + rwx_wait_for_ci_run: + description: "Poll and wait for an RWX CI run to complete or timeout" + parameters: + run_id: + description: "RWX run ID or full URL to wait for" + required: true + timeout_seconds: + description: "Maximum time to wait in seconds (default: 1800)" + poll_interval_seconds: + description: "Seconds between status checks (default: 30)" + rwx_get_recent_runs: + description: "Get recent CI/CD runs for a git branch. Returns all workflow runs by default; use definition_path to filter to a specific workflow." + parameters: + ref: + description: "Git ref (branch name) to filter runs by" + required: true + limit: + description: "Number of runs to return (default: 5)" + definition_path: + description: "Filter to a specific workflow file (e.g. .rwx/ci.yml, .rwx/auto-deploy.yml). Omit to return all workflows." + rwx_get_run_results: + description: "Get structured CI/CD pipeline results including per-task pass/fail status, failed tests, and build errors. Accepts a run ID or branch/commit lookup." + parameters: + run_id: + description: "RWX run ID or full URL (required unless branch is provided)" + task_key: + description: "Get results for a specific task by key (e.g. ci.checks.lint) instead of the full run" + branch: + description: "Look up results by branch name instead of run ID (uses current repo unless repo is set)" + commit: + description: "Look up results by commit SHA instead of run ID" + repo: + description: "Repository name for branch/commit lookup (default: current git repo)" + definition: + description: "Definition path for branch/commit lookup (e.g. .rwx/ci.yml)" + rwx_get_task_logs: + description: "Download and return full CI/CD task logs with build failure and test failure highlights" + parameters: + task_id: + description: "RWX task ID (32-char hex) or task URL" + run_id: + description: "RWX run ID — use with task_key to resolve by key instead of ID (optional)" + task_key: + description: "Task key (e.g. ci.checks.lint) — requires run_id. Use instead of task_id." + rwx_head_logs: + description: "Return the first N lines of logs for a run or task. Supports pagination via offset." + parameters: + id: + description: "RWX run ID or task ID" + required: true + lines: + description: "Number of lines to return from the beginning (default: 50, max: 50)" + offset: + description: "Line offset to start from (default: 0). Use for pagination." + task_key: + description: "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + rwx_tail_logs: + description: "Return the last N lines of logs for a run or task. Supports pagination via offset." + parameters: + id: + description: "RWX run ID or task ID" + required: true + lines: + description: "Number of lines to return from the end (default: 50, max: 50)" + offset: + description: "Line offset from the end (default: 0). Use for pagination to see earlier lines." + task_key: + description: "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + rwx_grep_logs: + description: "Search CI/CD build and test logs for a pattern with context lines. Results are paginated (50 lines per page)." + parameters: + id: + description: "RWX run ID or task ID" + required: true + pattern: + description: "Pattern to search for in the logs (case-insensitive)" + required: true + context: + description: "Number of context lines before and after matches (default: 3)" + page: + description: "Page number (default: 1). Each page returns up to 50 lines of output." + task_key: + description: "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + rwx_get_artifacts: + description: "List or download artifacts for a run" + parameters: + run_id: + description: "RWX run ID or full URL to get artifacts for" + required: true + download: + description: "Download artifacts (true/false, default: false — just list)" + artifact_key: + description: "Specific artifact key to download (optional, downloads all if not specified)" + rwx_validate_workflow: + description: "Validate an RWX workflow YAML file using the rwx CLI" + parameters: + file_path: + description: "Path to the RWX workflow YAML file to validate (default: .rwx/ci.yml)" + rwx_docs_search: + description: "Search RWX documentation. Use to find docs on caching, parallelism, configuration, and other RWX features." + parameters: + query: + description: "Search query (e.g. 'caching', 'parallelism', 'filtering files')" + required: true + limit: + description: "Maximum number of results (default: 5)" + rwx_docs_pull: + description: "Fetch an RWX documentation article as markdown. Use a URL from rwx_docs_search results or a docs path like /docs/caching." + parameters: + url_or_path: + description: "Full URL (https://www.rwx.com/docs/...) or path (/docs/caching) of the article to fetch" + required: true + rwx_vaults_var_show: + description: "Show a variable value from an RWX vault" + parameters: + name: + description: "Variable name to show" + required: true + vault: + description: "Vault name (default: 'default')" + rwx_vaults_var_set: + description: "Set a variable in an RWX vault" + parameters: + name: + description: "Variable name" + required: true + value: + description: "Variable value" + required: true + vault: + description: "Vault name (default: 'default')" + rwx_vaults_var_delete: + description: "Delete a variable from an RWX vault" + parameters: + name: + description: "Variable name to delete" + required: true + vault: + description: "Vault name (default: 'default')" + rwx_vaults_secret_set: + description: "Set a secret in an RWX vault. The value is stored encrypted and cannot be read back." + parameters: + name: + description: "Secret name" + required: true + value: + description: "Secret value" + required: true + vault: + description: "Vault name (default: 'default')" + rwx_vaults_secret_delete: + description: "Delete a secret from an RWX vault" + parameters: + name: + description: "Secret name to delete" + required: true + vault: + description: "Vault name (default: 'default')" + rwx_verify_cli: + description: "Verify the rwx CLI is installed and meets the minimum version requirement (>= 3.13.0)" + parameters: {} diff --git a/integrations/salesforce/tools.go b/integrations/salesforce/tools.go index 37756b9c..d90d311e 100644 --- a/integrations/salesforce/tools.go +++ b/integrations/salesforce/tools.go @@ -1,129 +1,12 @@ package salesforce -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── SObject Describe ──────────────────────────────────────────── - { - Name: mcp.ToolName("salesforce_describe_global"), Description: "List all SObjects available in the org with metadata (name, label, keyPrefix, queryable, createable, etc.). Start here for schema discovery.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("salesforce_describe_sobject"), Description: "Get detailed metadata for an SObject type including all fields, record types, child relationships, and URLs", - Parameters: map[string]string{"sobject": "SObject type name (e.g. Account, Contact, Lead, Opportunity, Case, custom__c)"}, - Required: []string{"sobject"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── SObject CRUD ──────────────────────────────────────────────── - { - Name: mcp.ToolName("salesforce_get_record"), Description: "Get a single Salesforce record by ID. Optionally specify fields to return.", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact, Lead, Opportunity)", - "id": "Record ID (15 or 18 character Salesforce ID)", - "fields": "Comma-separated field names to return (e.g. Id,Name,Email). Returns all fields if omitted.", - }, - Required: []string{"sobject", "id"}, - }, - { - Name: mcp.ToolName("salesforce_create_record"), Description: "Create a new Salesforce record. Provide field values as a JSON object.", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact, Lead, Opportunity)", - "data": "JSON string of field name/value pairs (e.g. {\"Name\":\"Acme\",\"Industry\":\"Technology\"})", - }, - Required: []string{"sobject", "data"}, - }, - { - Name: mcp.ToolName("salesforce_update_record"), Description: "Update an existing Salesforce record. Provide only the fields to change.", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact, Lead, Opportunity)", - "id": "Record ID (15 or 18 character Salesforce ID)", - "data": "JSON string of field name/value pairs to update", - }, - Required: []string{"sobject", "id", "data"}, - }, - { - Name: mcp.ToolName("salesforce_delete_record"), Description: "Delete a Salesforce record by ID", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact, Lead, Opportunity)", - "id": "Record ID (15 or 18 character Salesforce ID)", - }, - Required: []string{"sobject", "id"}, - }, - { - Name: mcp.ToolName("salesforce_get_record_by_external_id"), Description: "Get a Salesforce record using an external ID field value", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact)", - "field": "External ID field name", - "value": "External ID value", - }, - Required: []string{"sobject", "field", "value"}, - }, - { - Name: mcp.ToolName("salesforce_upsert_by_external_id"), Description: "Create or update a record using an external ID. Creates if not found, updates if exists.", - Parameters: map[string]string{ - "sobject": "SObject type (e.g. Account, Contact)", - "field": "External ID field name", - "value": "External ID value", - "data": "JSON string of field name/value pairs", - }, - Required: []string{"sobject", "field", "value", "data"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Queries ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("salesforce_query"), Description: "Execute a SOQL query to retrieve Salesforce records. Supports SELECT, WHERE, ORDER BY, LIMIT, GROUP BY, and relationship queries.", - Parameters: map[string]string{ - "q": "SOQL query string (e.g. SELECT Id, Name FROM Account WHERE Industry = 'Technology' LIMIT 10)", - }, - Required: []string{"q"}, - }, - { - Name: mcp.ToolName("salesforce_query_more"), Description: "Retrieve the next batch of SOQL query results using a nextRecordsUrl from a previous query response", - Parameters: map[string]string{ - "next_url": "The nextRecordsUrl value from a previous query response (e.g. /services/data/v62.0/query/01gxx0000...)", - }, - Required: []string{"next_url"}, - }, - { - Name: mcp.ToolName("salesforce_search"), Description: "Execute a SOSL full-text search across multiple Salesforce objects. Use FIND clause with search terms.", - Parameters: map[string]string{ - "q": "SOSL search string (e.g. FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name), Contact(Id, Name, Email))", - }, - Required: []string{"q"}, - }, - - // ── Metadata & Org ────────────────────────────────────────────── - { - Name: mcp.ToolName("salesforce_list_api_versions"), Description: "List all available Salesforce REST API versions", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("salesforce_get_limits"), Description: "Get org API usage limits and current consumption (DailyApiRequests, DailyBulkApiRequests, etc.)", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("salesforce_list_recently_viewed"), Description: "List recently viewed records across all object types or for a specific object type", - Parameters: map[string]string{ - "limit": "Max number of records to return (default 200)", - }, - }, - - // ── Composite ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("salesforce_composite_batch"), Description: "Execute up to 25 independent subrequests in a single API call. Each subrequest is a REST API request.", - Parameters: map[string]string{ - "requests": "JSON array of batch requests, each with method, url, and optional richInput (e.g. [{\"method\":\"GET\",\"url\":\"/services/data/v62.0/sobjects/Account/001xx\"}])", - }, - Required: []string{"requests"}, - }, - { - Name: mcp.ToolName("salesforce_sobject_collections"), Description: "Create, update, or delete up to 200 records of the same or different types in a single request", - Parameters: map[string]string{ - "method": "HTTP method: POST (create), PATCH (update), or DELETE (delete)", - "records": "JSON array of records with attributes.type for create/update (e.g. [{\"attributes\":{\"type\":\"Account\"},\"Name\":\"Acme\"}])", - "ids": "Comma-separated record IDs (required for DELETE only)", - "all_or_none": "If true, roll back all if any fail (default false)", - }, - Required: []string{"method"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/salesforce/tools.yaml b/integrations/salesforce/tools.yaml new file mode 100644 index 00000000..76108b56 --- /dev/null +++ b/integrations/salesforce/tools.yaml @@ -0,0 +1,126 @@ +version: 1 +tools: + salesforce_describe_global: + description: "List all SObjects available in the org with metadata (name, label, keyPrefix, queryable, createable, etc.). Start here for schema discovery." + parameters: {} + salesforce_describe_sobject: + description: "Get detailed metadata for an SObject type including all fields, record types, child relationships, and URLs" + parameters: + sobject: + description: "SObject type name (e.g. Account, Contact, Lead, Opportunity, Case, custom__c)" + required: true + salesforce_get_record: + description: "Get a single Salesforce record by ID. Optionally specify fields to return." + parameters: + sobject: + description: "SObject type (e.g. Account, Contact, Lead, Opportunity)" + required: true + id: + description: "Record ID (15 or 18 character Salesforce ID)" + required: true + fields: + description: "Comma-separated field names to return (e.g. Id,Name,Email). Returns all fields if omitted." + salesforce_create_record: + description: "Create a new Salesforce record. Provide field values as a JSON object." + parameters: + sobject: + description: "SObject type (e.g. Account, Contact, Lead, Opportunity)" + required: true + data: + description: 'JSON string of field name/value pairs (e.g. {"Name":"Acme","Industry":"Technology"})' + required: true + salesforce_update_record: + description: "Update an existing Salesforce record. Provide only the fields to change." + parameters: + sobject: + description: "SObject type (e.g. Account, Contact, Lead, Opportunity)" + required: true + id: + description: "Record ID (15 or 18 character Salesforce ID)" + required: true + data: + description: "JSON string of field name/value pairs to update" + required: true + salesforce_delete_record: + description: "Delete a Salesforce record by ID" + parameters: + sobject: + description: "SObject type (e.g. Account, Contact, Lead, Opportunity)" + required: true + id: + description: "Record ID (15 or 18 character Salesforce ID)" + required: true + salesforce_get_record_by_external_id: + description: "Get a Salesforce record using an external ID field value" + parameters: + sobject: + description: "SObject type (e.g. Account, Contact)" + required: true + field: + description: "External ID field name" + required: true + value: + description: "External ID value" + required: true + salesforce_upsert_by_external_id: + description: "Create or update a record using an external ID. Creates if not found, updates if exists." + parameters: + sobject: + description: "SObject type (e.g. Account, Contact)" + required: true + field: + description: "External ID field name" + required: true + value: + description: "External ID value" + required: true + data: + description: "JSON string of field name/value pairs" + required: true + salesforce_query: + description: "Execute a SOQL query to retrieve Salesforce records. Supports SELECT, WHERE, ORDER BY, LIMIT, GROUP BY, and relationship queries." + parameters: + q: + description: "SOQL query string (e.g. SELECT Id, Name FROM Account WHERE Industry = 'Technology' LIMIT 10)" + required: true + salesforce_query_more: + description: "Retrieve the next batch of SOQL query results using a nextRecordsUrl from a previous query response" + parameters: + next_url: + description: "The nextRecordsUrl value from a previous query response (e.g. /services/data/v62.0/query/01gxx0000...)" + required: true + salesforce_search: + description: "Execute a SOSL full-text search across multiple Salesforce objects. Use FIND clause with search terms." + parameters: + q: + description: "SOSL search string (e.g. FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name), Contact(Id, Name, Email))" + required: true + salesforce_list_api_versions: + description: "List all available Salesforce REST API versions" + parameters: {} + salesforce_get_limits: + description: "Get org API usage limits and current consumption (DailyApiRequests, DailyBulkApiRequests, etc.)" + parameters: {} + salesforce_list_recently_viewed: + description: "List recently viewed records across all object types or for a specific object type" + parameters: + limit: + description: "Max number of records to return (default 200)" + salesforce_composite_batch: + description: "Execute up to 25 independent subrequests in a single API call. Each subrequest is a REST API request." + parameters: + requests: + description: 'JSON array of batch requests, each with method, url, and optional richInput (e.g. [{"method":"GET","url":"/services/data/v62.0/sobjects/Account/001xx"}])' + required: true + salesforce_sobject_collections: + description: "Create, update, or delete up to 200 records of the same or different types in a single request" + parameters: + method: + description: "HTTP method: POST (create), PATCH (update), or DELETE (delete)" + required: true + records: + description: 'JSON array of records with attributes.type for create/update (e.g. [{"attributes":{"type":"Account"},"Name":"Acme"}])' + ids: + description: "Comma-separated record IDs (required for DELETE only)" + all_or_none: + description: "If true, roll back all if any fail (default false)" diff --git a/integrations/sentry/tools.go b/integrations/sentry/tools.go index dc5a2783..f93ddc3c 100644 --- a/integrations/sentry/tools.go +++ b/integrations/sentry/tools.go @@ -1,281 +1,12 @@ package sentry -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Organizations ──────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_get_organization"), Description: "Get details of the Sentry organization", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("sentry_list_org_projects"), Description: "List all projects in the organization", - Parameters: map[string]string{"cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_list_org_teams"), Description: "List all teams in the organization", - Parameters: map[string]string{"cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_list_org_members"), Description: "List members of the organization", - Parameters: map[string]string{"cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_get_org_member"), Description: "Get details of an organization member", - Parameters: map[string]string{"member_id": "Member ID (or 'me' for current user)"}, - Required: []string{"member_id"}, - }, - { - Name: mcp.ToolName("sentry_list_org_repos"), Description: "List repositories connected to the organization", - Parameters: map[string]string{"cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_resolve_short_id"), Description: "Resolve a Sentry short ID (e.g., PROJECT-123) to full error issue details. Use to look up a bug by its short reference.", - Parameters: map[string]string{"short_id": "Short ID (e.g., PROJECT-123)"}, - Required: []string{"short_id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Projects ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_projects"), Description: "List all projects accessible to the auth token", - Parameters: map[string]string{"cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_get_project"), Description: "Get details of a specific project", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_update_project"), Description: "Update a project's settings", - Parameters: map[string]string{"project": "Project slug", "name": "New name", "slug": "New slug", "platform": "Platform (e.g., python, javascript)", "isBookmarked": "Bookmark project (true/false)"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_delete_project"), Description: "Delete a project", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_create_project"), Description: "Create a new project under a team", - Parameters: map[string]string{"team": "Team slug", "name": "Project name", "slug": "Project slug (optional, auto-generated from name)", "platform": "Platform (e.g., python, javascript)"}, - Required: []string{"team", "name"}, - }, - { - Name: mcp.ToolName("sentry_list_project_keys"), Description: "List a project's client keys (DSN)", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_list_project_envs"), Description: "List a project's environments", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_list_project_tags"), Description: "List tags for a project", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_get_project_stats"), Description: "Get error and crash event count statistics for a project. Use to monitor error rate and volume.", - Parameters: map[string]string{"project": "Project slug", "stat": "Stat type: received, rejected, blacklisted, generated (default: received)", "since": "Unix timestamp for start", "until": "Unix timestamp for end", "resolution": "Resolution in seconds (e.g., 3600 for hourly)"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_list_project_hooks"), Description: "List service hooks for a project", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Teams ──────────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_get_team"), Description: "Get details of a specific team", - Parameters: map[string]string{"team": "Team slug"}, - Required: []string{"team"}, - }, - { - Name: mcp.ToolName("sentry_create_team"), Description: "Create a new team in the organization", - Parameters: map[string]string{"name": "Team name", "slug": "Team slug (optional)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("sentry_delete_team"), Description: "Delete a team", - Parameters: map[string]string{"team": "Team slug"}, - Required: []string{"team"}, - }, - { - Name: mcp.ToolName("sentry_list_team_projects"), Description: "List projects belonging to a team", - Parameters: map[string]string{"team": "Team slug", "cursor": "Pagination cursor"}, - Required: []string{"team"}, - }, - - // ── Issues & Events ────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_issues"), Description: "List errors and exceptions for a project. Start here for error tracking, debugging, and finding unresolved bugs or crashes.", - Parameters: map[string]string{"project": "Project slug", "query": "Search query (e.g., 'is:unresolved', 'assigned:me')", "cursor": "Pagination cursor", "sort": "Sort: date, new, freq, user (default: date)", "statsPeriod": "Stats period: '' (default), '24h', or '14d'"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_get_issue"), Description: "Get details of a specific error or exception issue, including stacktrace and debugging context", - Parameters: map[string]string{"issue_id": "Issue ID"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("sentry_update_issue"), Description: "Update an error issue (resolve, assign, triage, etc.)", - Parameters: map[string]string{"issue_id": "Issue ID", "status": "Status: resolved, unresolved, ignored", "assignedTo": "Assign to user (email or username, empty to unassign)", "hasSeen": "Mark as seen (true/false)", "isBookmarked": "Bookmark (true/false)", "isSubscribed": "Subscribe (true/false)", "isPublic": "Make public (true/false)"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_issue"), Description: "Delete an issue", - Parameters: map[string]string{"issue_id": "Issue ID"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("sentry_list_issue_events"), Description: "List error occurrences and crash events for a specific issue", - Parameters: map[string]string{"issue_id": "Issue ID", "cursor": "Pagination cursor"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("sentry_list_issue_hashes"), Description: "List hashes (fingerprints) for an issue", - Parameters: map[string]string{"issue_id": "Issue ID", "cursor": "Pagination cursor"}, - Required: []string{"issue_id"}, - }, - { - Name: mcp.ToolName("sentry_get_issue_tag_values"), Description: "Get tag value distribution for an issue", - Parameters: map[string]string{"issue_id": "Issue ID", "tag_name": "Tag key (e.g., browser, os, url, environment)"}, - Required: []string{"issue_id", "tag_name"}, - }, - { - Name: mcp.ToolName("sentry_list_project_events"), Description: "List error and exception events for a project. Use to investigate crashes and debug production issues.", - Parameters: map[string]string{"project": "Project slug", "cursor": "Pagination cursor"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_get_event"), Description: "Get details of a specific event", - Parameters: map[string]string{"project": "Project slug", "event_id": "Event ID"}, - Required: []string{"project", "event_id"}, - }, - { - Name: mcp.ToolName("sentry_list_org_issues"), Description: "List error and exception issues across the entire organization. Search all projects for bugs, crashes, and unresolved problems.", - Parameters: map[string]string{"query": "Search query (e.g., 'is:unresolved level:error')", "project": "Filter by project slug", "cursor": "Pagination cursor", "sort": "Sort: date, new, freq, user", "statsPeriod": "Stats period: '' (default), '24h', or '14d'"}, - }, - - // ── Releases ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_releases"), Description: "List releases for the organization", - Parameters: map[string]string{"query": "Filter releases by version", "project": "Filter by project slug", "cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_get_release"), Description: "Get details of a specific release", - Parameters: map[string]string{"version": "Release version identifier"}, - Required: []string{"version"}, - }, - { - Name: mcp.ToolName("sentry_create_release"), Description: "Create a new release", - Parameters: map[string]string{"version": "Release version", "ref": "Git ref (commit SHA or tag)", "url": "URL for the release", "projects": "Comma-separated project slugs", "dateReleased": "Release date (ISO 8601)"}, - Required: []string{"version", "projects"}, - }, - { - Name: mcp.ToolName("sentry_delete_release"), Description: "Delete a release", - Parameters: map[string]string{"version": "Release version"}, - Required: []string{"version"}, - }, - { - Name: mcp.ToolName("sentry_list_release_commits"), Description: "List commits in a release", - Parameters: map[string]string{"version": "Release version", "cursor": "Pagination cursor"}, - Required: []string{"version"}, - }, - { - Name: mcp.ToolName("sentry_list_release_deploys"), Description: "List deploys for a release", - Parameters: map[string]string{"version": "Release version"}, - Required: []string{"version"}, - }, - { - Name: mcp.ToolName("sentry_create_deploy"), Description: "Create a deploy for a release", - Parameters: map[string]string{"version": "Release version", "environment": "Environment name (e.g., production)", "name": "Deploy name", "url": "Deploy URL", "dateStarted": "Start time (ISO 8601)", "dateFinished": "Finish time (ISO 8601)"}, - Required: []string{"version", "environment"}, - }, - { - Name: mcp.ToolName("sentry_list_release_files"), Description: "List files (artifacts) in a release", - Parameters: map[string]string{"version": "Release version", "cursor": "Pagination cursor"}, - Required: []string{"version"}, - }, - - // ── Alerts ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_metric_alerts"), Description: "List metric alert rules for monitoring thresholds and warnings across the organization", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("sentry_get_metric_alert"), Description: "Get a specific metric alert rule", - Parameters: map[string]string{"alert_rule_id": "Alert rule ID"}, - Required: []string{"alert_rule_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_metric_alert"), Description: "Delete a metric alert rule", - Parameters: map[string]string{"alert_rule_id": "Alert rule ID"}, - Required: []string{"alert_rule_id"}, - }, - { - Name: mcp.ToolName("sentry_list_issue_alerts"), Description: "List error alert rules that trigger notifications for a project", - Parameters: map[string]string{"project": "Project slug"}, - Required: []string{"project"}, - }, - { - Name: mcp.ToolName("sentry_get_issue_alert"), Description: "Get a specific issue alert rule", - Parameters: map[string]string{"project": "Project slug", "alert_rule_id": "Alert rule ID"}, - Required: []string{"project", "alert_rule_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_issue_alert"), Description: "Delete an issue alert rule", - Parameters: map[string]string{"project": "Project slug", "alert_rule_id": "Alert rule ID"}, - Required: []string{"project", "alert_rule_id"}, - }, - - // ── Monitors (Cron) ────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_monitors"), Description: "List cron monitors for the organization", - Parameters: map[string]string{"project": "Filter by project slug", "cursor": "Pagination cursor"}, - }, - { - Name: mcp.ToolName("sentry_get_monitor"), Description: "Get a specific cron monitor", - Parameters: map[string]string{"project": "Project slug", "monitor_id": "Monitor ID or slug"}, - Required: []string{"project", "monitor_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_monitor"), Description: "Delete a cron monitor", - Parameters: map[string]string{"project": "Project slug", "monitor_id": "Monitor ID or slug"}, - Required: []string{"project", "monitor_id"}, - }, - - // ── Discover ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_saved_queries"), Description: "List saved Discover queries", - Parameters: map[string]string{"cursor": "Pagination cursor", "sortBy": "Sort by: dateCreated, dateUpdated, name, myqueries (default: dateUpdated)"}, - }, - { - Name: mcp.ToolName("sentry_get_saved_query"), Description: "Get a specific saved Discover query", - Parameters: map[string]string{"query_id": "Saved query ID"}, - Required: []string{"query_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_saved_query"), Description: "Delete a saved Discover query", - Parameters: map[string]string{"query_id": "Saved query ID"}, - Required: []string{"query_id"}, - }, - - // ── Replays ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("sentry_list_replays"), Description: "List session replay recordings. Use to visually reproduce and investigate specific user sessions.", - Parameters: map[string]string{"query": "Search query", "cursor": "Pagination cursor", "limit": "Max results (default 50)", "statsPeriod": "Stats period: '' (default), '24h', or '14d'"}, - }, - { - Name: mcp.ToolName("sentry_get_replay"), Description: "Get details of a specific replay", - Parameters: map[string]string{"replay_id": "Replay ID"}, - Required: []string{"replay_id"}, - }, - { - Name: mcp.ToolName("sentry_delete_replay"), Description: "Delete a replay", - Parameters: map[string]string{"replay_id": "Replay ID"}, - Required: []string{"replay_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/sentry/tools.yaml b/integrations/sentry/tools.yaml new file mode 100644 index 00000000..3f132182 --- /dev/null +++ b/integrations/sentry/tools.yaml @@ -0,0 +1,480 @@ +version: 1 +tools: + sentry_get_organization: + description: "Get details of the Sentry organization" + parameters: {} + + sentry_list_org_projects: + description: "List all projects in the organization" + parameters: + cursor: + description: "Pagination cursor" + + sentry_list_org_teams: + description: "List all teams in the organization" + parameters: + cursor: + description: "Pagination cursor" + + sentry_list_org_members: + description: "List members of the organization" + parameters: + cursor: + description: "Pagination cursor" + + sentry_get_org_member: + description: "Get details of an organization member" + parameters: + member_id: + description: "Member ID (or 'me' for current user)" + required: true + + sentry_list_org_repos: + description: "List repositories connected to the organization" + parameters: + cursor: + description: "Pagination cursor" + + sentry_resolve_short_id: + description: "Resolve a Sentry short ID (e.g., PROJECT-123) to full error issue details. Use to look up a bug by its short reference." + parameters: + short_id: + description: "Short ID (e.g., PROJECT-123)" + required: true + + sentry_list_projects: + description: "List all projects accessible to the auth token" + parameters: + cursor: + description: "Pagination cursor" + + sentry_get_project: + description: "Get details of a specific project" + parameters: + project: + description: "Project slug" + required: true + + sentry_update_project: + description: "Update a project's settings" + parameters: + project: + description: "Project slug" + required: true + name: + description: "New name" + slug: + description: "New slug" + platform: + description: "Platform (e.g., python, javascript)" + isBookmarked: + description: "Bookmark project (true/false)" + + sentry_delete_project: + description: "Delete a project" + parameters: + project: + description: "Project slug" + required: true + + sentry_create_project: + description: "Create a new project under a team" + parameters: + team: + description: "Team slug" + required: true + name: + description: "Project name" + required: true + slug: + description: "Project slug (optional, auto-generated from name)" + platform: + description: "Platform (e.g., python, javascript)" + + sentry_list_project_keys: + description: "List a project's client keys (DSN)" + parameters: + project: + description: "Project slug" + required: true + + sentry_list_project_envs: + description: "List a project's environments" + parameters: + project: + description: "Project slug" + required: true + + sentry_list_project_tags: + description: "List tags for a project" + parameters: + project: + description: "Project slug" + required: true + + sentry_get_project_stats: + description: "Get error and crash event count statistics for a project. Use to monitor error rate and volume." + parameters: + project: + description: "Project slug" + required: true + stat: + description: "Stat type: received, rejected, blacklisted, generated (default: received)" + since: + description: "Unix timestamp for start" + until: + description: "Unix timestamp for end" + resolution: + description: "Resolution in seconds (e.g., 3600 for hourly)" + + sentry_list_project_hooks: + description: "List service hooks for a project" + parameters: + project: + description: "Project slug" + required: true + + sentry_get_team: + description: "Get details of a specific team" + parameters: + team: + description: "Team slug" + required: true + + sentry_create_team: + description: "Create a new team in the organization" + parameters: + name: + description: "Team name" + required: true + slug: + description: "Team slug (optional)" + + sentry_delete_team: + description: "Delete a team" + parameters: + team: + description: "Team slug" + required: true + + sentry_list_team_projects: + description: "List projects belonging to a team" + parameters: + team: + description: "Team slug" + required: true + cursor: + description: "Pagination cursor" + + sentry_list_issues: + description: "List errors and exceptions for a project. Start here for error tracking, debugging, and finding unresolved bugs or crashes." + parameters: + project: + description: "Project slug" + required: true + query: + description: "Search query (e.g., 'is:unresolved', 'assigned:me')" + cursor: + description: "Pagination cursor" + sort: + description: "Sort: date, new, freq, user (default: date)" + statsPeriod: + description: "Stats period: '' (default), '24h', or '14d'" + + sentry_get_issue: + description: "Get details of a specific error or exception issue, including stacktrace and debugging context" + parameters: + issue_id: + description: "Issue ID" + required: true + + sentry_update_issue: + description: "Update an error issue (resolve, assign, triage, etc.)" + parameters: + issue_id: + description: "Issue ID" + required: true + status: + description: "Status: resolved, unresolved, ignored" + assignedTo: + description: "Assign to user (email or username, empty to unassign)" + hasSeen: + description: "Mark as seen (true/false)" + isBookmarked: + description: "Bookmark (true/false)" + isSubscribed: + description: "Subscribe (true/false)" + isPublic: + description: "Make public (true/false)" + + sentry_delete_issue: + description: "Delete an issue" + parameters: + issue_id: + description: "Issue ID" + required: true + + sentry_list_issue_events: + description: "List error occurrences and crash events for a specific issue" + parameters: + issue_id: + description: "Issue ID" + required: true + cursor: + description: "Pagination cursor" + + sentry_list_issue_hashes: + description: "List hashes (fingerprints) for an issue" + parameters: + issue_id: + description: "Issue ID" + required: true + cursor: + description: "Pagination cursor" + + sentry_get_issue_tag_values: + description: "Get tag value distribution for an issue" + parameters: + issue_id: + description: "Issue ID" + required: true + tag_name: + description: "Tag key (e.g., browser, os, url, environment)" + required: true + + sentry_list_project_events: + description: "List error and exception events for a project. Use to investigate crashes and debug production issues." + parameters: + project: + description: "Project slug" + required: true + cursor: + description: "Pagination cursor" + + sentry_get_event: + description: "Get details of a specific event" + parameters: + project: + description: "Project slug" + required: true + event_id: + description: "Event ID" + required: true + + sentry_list_org_issues: + description: "List error and exception issues across the entire organization. Search all projects for bugs, crashes, and unresolved problems." + parameters: + query: + description: "Search query (e.g., 'is:unresolved level:error')" + project: + description: "Filter by project slug" + cursor: + description: "Pagination cursor" + sort: + description: "Sort: date, new, freq, user" + statsPeriod: + description: "Stats period: '' (default), '24h', or '14d'" + + sentry_list_releases: + description: "List releases for the organization" + parameters: + query: + description: "Filter releases by version" + project: + description: "Filter by project slug" + cursor: + description: "Pagination cursor" + + sentry_get_release: + description: "Get details of a specific release" + parameters: + version: + description: "Release version identifier" + required: true + + sentry_create_release: + description: "Create a new release" + parameters: + version: + description: "Release version" + required: true + ref: + description: "Git ref (commit SHA or tag)" + url: + description: "URL for the release" + projects: + description: "Comma-separated project slugs" + required: true + dateReleased: + description: "Release date (ISO 8601)" + + sentry_delete_release: + description: "Delete a release" + parameters: + version: + description: "Release version" + required: true + + sentry_list_release_commits: + description: "List commits in a release" + parameters: + version: + description: "Release version" + required: true + cursor: + description: "Pagination cursor" + + sentry_list_release_deploys: + description: "List deploys for a release" + parameters: + version: + description: "Release version" + required: true + + sentry_create_deploy: + description: "Create a deploy for a release" + parameters: + version: + description: "Release version" + required: true + environment: + description: "Environment name (e.g., production)" + required: true + name: + description: "Deploy name" + url: + description: "Deploy URL" + dateStarted: + description: "Start time (ISO 8601)" + dateFinished: + description: "Finish time (ISO 8601)" + + sentry_list_release_files: + description: "List files (artifacts) in a release" + parameters: + version: + description: "Release version" + required: true + cursor: + description: "Pagination cursor" + + sentry_list_metric_alerts: + description: "List metric alert rules for monitoring thresholds and warnings across the organization" + parameters: {} + + sentry_get_metric_alert: + description: "Get a specific metric alert rule" + parameters: + alert_rule_id: + description: "Alert rule ID" + required: true + + sentry_delete_metric_alert: + description: "Delete a metric alert rule" + parameters: + alert_rule_id: + description: "Alert rule ID" + required: true + + sentry_list_issue_alerts: + description: "List error alert rules that trigger notifications for a project" + parameters: + project: + description: "Project slug" + required: true + + sentry_get_issue_alert: + description: "Get a specific issue alert rule" + parameters: + project: + description: "Project slug" + required: true + alert_rule_id: + description: "Alert rule ID" + required: true + + sentry_delete_issue_alert: + description: "Delete an issue alert rule" + parameters: + project: + description: "Project slug" + required: true + alert_rule_id: + description: "Alert rule ID" + required: true + + sentry_list_monitors: + description: "List cron monitors for the organization" + parameters: + project: + description: "Filter by project slug" + cursor: + description: "Pagination cursor" + + sentry_get_monitor: + description: "Get a specific cron monitor" + parameters: + project: + description: "Project slug" + required: true + monitor_id: + description: "Monitor ID or slug" + required: true + + sentry_delete_monitor: + description: "Delete a cron monitor" + parameters: + project: + description: "Project slug" + required: true + monitor_id: + description: "Monitor ID or slug" + required: true + + sentry_list_saved_queries: + description: "List saved Discover queries" + parameters: + cursor: + description: "Pagination cursor" + sortBy: + description: "Sort by: dateCreated, dateUpdated, name, myqueries (default: dateUpdated)" + + sentry_get_saved_query: + description: "Get a specific saved Discover query" + parameters: + query_id: + description: "Saved query ID" + required: true + + sentry_delete_saved_query: + description: "Delete a saved Discover query" + parameters: + query_id: + description: "Saved query ID" + required: true + + sentry_list_replays: + description: "List session replay recordings. Use to visually reproduce and investigate specific user sessions." + parameters: + query: + description: "Search query" + cursor: + description: "Pagination cursor" + limit: + description: "Max results (default 50)" + statsPeriod: + description: "Stats period: '' (default), '24h', or '14d'" + + sentry_get_replay: + description: "Get details of a specific replay" + parameters: + replay_id: + description: "Replay ID" + required: true + + sentry_delete_replay: + description: "Delete a replay" + parameters: + replay_id: + description: "Replay ID" + required: true diff --git a/integrations/signoz/tools.go b/integrations/signoz/tools.go index ba347cd7..aaa567b5 100644 --- a/integrations/signoz/tools.go +++ b/integrations/signoz/tools.go @@ -1,169 +1,12 @@ package signoz -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Services ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_list_services"), Description: "List all application services and their metrics (latency, error rate, throughput). Start here for APM, service health monitoring, and performance debugging.", - Parameters: map[string]string{"start": "Start time in epoch milliseconds", "end": "End time in epoch milliseconds"}, - Required: []string{"start", "end"}, - }, - { - Name: mcp.ToolName("signoz_get_service_overview"), Description: "Get detailed overview metrics for a specific service over a time range. Use after list_services for latency percentiles, error rates, and request counts.", - Parameters: map[string]string{"service": "Service name", "start": "Start time in epoch milliseconds", "end": "End time in epoch milliseconds", "step": "Step interval in seconds (default: 60)"}, - Required: []string{"service", "start", "end"}, - }, - { - Name: mcp.ToolName("signoz_top_operations"), Description: "Get top operations for a service ranked by latency, error rate, or call count. Use after list_services to drill into hotspot endpoints and slow operations.", - Parameters: map[string]string{"service": "Service name", "start": "Start time in epoch milliseconds", "end": "End time in epoch milliseconds"}, - Required: []string{"service", "start", "end"}, - }, - { - Name: mcp.ToolName("signoz_top_level_operations"), Description: "Get top-level (entry point) operations across all services. Useful for finding the most called API endpoints and root spans.", - Parameters: map[string]string{"start": "Start time in epoch milliseconds", "end": "End time in epoch milliseconds"}, - Required: []string{"start", "end"}, - }, - { - Name: mcp.ToolName("signoz_entry_point_operations"), Description: "Get entry point operations for a service (v2). Returns the first spans in a trace for each service.", - Parameters: map[string]string{"service": "Service name", "start": "Start time in epoch milliseconds", "end": "End time in epoch milliseconds"}, - Required: []string{"service", "start", "end"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Query: Logs ────────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_search_logs"), Description: "Search and filter log entries. Supports attribute filters (severity, service, body text), ordering, and pagination. Start here for log exploration, debugging, and error investigation.", - Parameters: map[string]string{ - "start": "Start time in epoch milliseconds", - "end": "End time in epoch milliseconds", - "filter": "Single filter expression: key op value, e.g. \"severity_text = 'ERROR'\". Only one expression per call.", - "limit": "Max logs to return (default: 20, max: 100)", - "offset": "Offset for pagination (default: 0)", - }, - Required: []string{"start", "end"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Query: Traces ──────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_search_traces"), Description: "Search distributed traces with filters. Find slow requests, errors, and specific operations across services. Start here for trace exploration and latency debugging.", - Parameters: map[string]string{ - "start": "Start time in epoch milliseconds", - "end": "End time in epoch milliseconds", - "service": "Filter by service name", - "filter": "Single filter expression: key op value, e.g. \"hasError = true\". Only one expression per call; use service param for service filtering.", - "limit": "Max traces to return (default: 20, max: 100)", - "offset": "Offset for pagination (default: 0)", - }, - Required: []string{"start", "end"}, - }, - { - Name: mcp.ToolName("signoz_get_trace"), Description: "Get all spans for a specific trace by its trace ID. Shows the full request path across services with timing breakdown. Use after search_traces.", - Parameters: map[string]string{"trace_id": "Trace ID"}, - Required: []string{"trace_id"}, - }, - - // ── Query: Metrics ─────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_query_metrics"), Description: "Query time-series metrics data. Supports aggregation (avg, sum, min, max, count, rate, p50-p99), filtering, and group-by. Start here for metrics exploration, infrastructure monitoring, and custom dashboards.", - Parameters: map[string]string{ - "start": "Start time in epoch milliseconds", - "end": "End time in epoch milliseconds", - "metric_name": "Metric name, e.g. 'signoz_calls_total' or 'system.cpu.load_average.1m'", - "aggregate_op": "Aggregation: avg, sum, min, max, count, rate, p50, p75, p90, p95, p99 (default: avg)", - "filter": "Single filter expression: key op value, e.g. \"service_name = 'frontend'\". Only one expression per call.", - "group_by": "Comma-separated attribute keys to group by, e.g. 'service_name,host'", - "step": "Step interval in seconds (default: 60)", - }, - Required: []string{"start", "end", "metric_name"}, - }, - - // ── Dashboards ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_list_dashboards"), Description: "List all dashboards. Start here for dashboard discovery and visualization management.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("signoz_get_dashboard"), Description: "Get a specific dashboard by ID including all panels and widget configurations. Use after list_dashboards.", - Parameters: map[string]string{"id": "Dashboard ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("signoz_create_dashboard"), Description: "Create a new dashboard with title, description, and optional tags.", - Parameters: map[string]string{"title": "Dashboard title", "description": "Dashboard description", "tags": "Comma-separated tags for categorization"}, - Required: []string{"title"}, - }, - { - Name: mcp.ToolName("signoz_update_dashboard"), Description: "Update an existing dashboard. Send dashboard content (title, widgets, layout, tags) or the full get_dashboard response — nesting is auto-corrected. The API wraps content automatically.", - Parameters: map[string]string{"id": "Dashboard ID", "dashboard": "Dashboard content object with title, widgets, layout, tags, variables"}, - Required: []string{"id", "dashboard"}, - }, - { - Name: mcp.ToolName("signoz_delete_dashboard"), Description: "Delete a dashboard by ID.", - Parameters: map[string]string{"id": "Dashboard ID"}, - Required: []string{"id"}, - }, - - // ── Alerts (Rules) ─────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_list_alerts"), Description: "List all alert rules including their current state (firing, inactive, pending). Start here for alert management and on-call monitoring.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("signoz_get_alert"), Description: "Get a specific alert rule by ID with full configuration and current state. Use after list_alerts.", - Parameters: map[string]string{"id": "Alert rule ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("signoz_create_alert"), Description: "Create a new alert rule. Requires a full alert rule definition JSON body.", - Parameters: map[string]string{"rule": "Full alert rule definition JSON object"}, - Required: []string{"rule"}, - }, - { - Name: mcp.ToolName("signoz_update_alert"), Description: "Update an existing alert rule. Send the full rule object (get it first with get_alert).", - Parameters: map[string]string{"id": "Alert rule ID", "rule": "Full alert rule definition JSON object"}, - Required: []string{"id", "rule"}, - }, - { - Name: mcp.ToolName("signoz_delete_alert"), Description: "Delete an alert rule by ID.", - Parameters: map[string]string{"id": "Alert rule ID"}, - Required: []string{"id"}, - }, - - // ── Saved Views ────────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_list_saved_views"), Description: "List all saved explorer views for logs and traces. Use to find pre-configured query views.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("signoz_get_saved_view"), Description: "Get a specific saved view by ID with full query configuration. Use after list_saved_views.", - Parameters: map[string]string{"view_id": "Saved view ID"}, - Required: []string{"view_id"}, - }, - { - Name: mcp.ToolName("signoz_create_saved_view"), Description: "Create a new saved explorer view with query configuration.", - Parameters: map[string]string{"view": "Saved view definition JSON object"}, - Required: []string{"view"}, - }, - { - Name: mcp.ToolName("signoz_update_saved_view"), Description: "Update an existing saved view.", - Parameters: map[string]string{"view_id": "Saved view ID", "view": "Updated saved view definition JSON object"}, - Required: []string{"view_id", "view"}, - }, - { - Name: mcp.ToolName("signoz_delete_saved_view"), Description: "Delete a saved view by ID.", - Parameters: map[string]string{"view_id": "Saved view ID"}, - Required: []string{"view_id"}, - }, - - // ── Notification Channels ──────────────────────────────────────── - { - Name: mcp.ToolName("signoz_list_channels"), Description: "List all configured notification channels (Slack, email, PagerDuty, webhooks) for alerts.", - Parameters: map[string]string{}, - }, - - // ── Extras ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("signoz_get_version"), Description: "Get SigNoz server version, edition (community/enterprise), and setup status.", - Parameters: map[string]string{}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/signoz/tools.yaml b/integrations/signoz/tools.yaml new file mode 100644 index 00000000..1f7e4094 --- /dev/null +++ b/integrations/signoz/tools.yaml @@ -0,0 +1,216 @@ +version: 1 +tools: + signoz_list_services: + description: "List all application services and their metrics (latency, error rate, throughput). Start here for APM, service health monitoring, and performance debugging." + parameters: + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + signoz_get_service_overview: + description: "Get detailed overview metrics for a specific service over a time range. Use after list_services for latency percentiles, error rates, and request counts." + parameters: + service: + description: "Service name" + required: true + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + step: + description: "Step interval in seconds (default: 60)" + signoz_top_operations: + description: "Get top operations for a service ranked by latency, error rate, or call count. Use after list_services to drill into hotspot endpoints and slow operations." + parameters: + service: + description: "Service name" + required: true + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + signoz_top_level_operations: + description: "Get top-level (entry point) operations across all services. Useful for finding the most called API endpoints and root spans." + parameters: + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + signoz_entry_point_operations: + description: "Get entry point operations for a service (v2). Returns the first spans in a trace for each service." + parameters: + service: + description: "Service name" + required: true + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + signoz_search_logs: + description: "Search and filter log entries. Supports attribute filters (severity, service, body text), ordering, and pagination. Start here for log exploration, debugging, and error investigation." + parameters: + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + filter: + description: "Single filter expression: key op value, e.g. \"severity_text = 'ERROR'\". Only one expression per call." + limit: + description: "Max logs to return (default: 20, max: 100)" + offset: + description: "Offset for pagination (default: 0)" + signoz_search_traces: + description: "Search distributed traces with filters. Find slow requests, errors, and specific operations across services. Start here for trace exploration and latency debugging." + parameters: + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + service: + description: "Filter by service name" + filter: + description: "Single filter expression: key op value, e.g. \"hasError = true\". Only one expression per call; use service param for service filtering." + limit: + description: "Max traces to return (default: 20, max: 100)" + offset: + description: "Offset for pagination (default: 0)" + signoz_get_trace: + description: "Get all spans for a specific trace by its trace ID. Shows the full request path across services with timing breakdown. Use after search_traces." + parameters: + trace_id: + description: "Trace ID" + required: true + signoz_query_metrics: + description: "Query time-series metrics data. Supports aggregation (avg, sum, min, max, count, rate, p50-p99), filtering, and group-by. Start here for metrics exploration, infrastructure monitoring, and custom dashboards." + parameters: + start: + description: "Start time in epoch milliseconds" + required: true + end: + description: "End time in epoch milliseconds" + required: true + metric_name: + description: "Metric name, e.g. 'signoz_calls_total' or 'system.cpu.load_average.1m'" + required: true + aggregate_op: + description: "Aggregation: avg, sum, min, max, count, rate, p50, p75, p90, p95, p99 (default: avg)" + filter: + description: "Single filter expression: key op value, e.g. \"service_name = 'frontend'\". Only one expression per call." + group_by: + description: "Comma-separated attribute keys to group by, e.g. 'service_name,host'" + step: + description: "Step interval in seconds (default: 60)" + signoz_list_dashboards: + description: "List all dashboards. Start here for dashboard discovery and visualization management." + parameters: {} + signoz_get_dashboard: + description: "Get a specific dashboard by ID including all panels and widget configurations. Use after list_dashboards." + parameters: + id: + description: "Dashboard ID" + required: true + signoz_create_dashboard: + description: "Create a new dashboard with title, description, and optional tags." + parameters: + title: + description: "Dashboard title" + required: true + description: + description: "Dashboard description" + tags: + description: "Comma-separated tags for categorization" + signoz_update_dashboard: + description: "Update an existing dashboard. Send dashboard content (title, widgets, layout, tags) or the full get_dashboard response — nesting is auto-corrected. The API wraps content automatically." + parameters: + id: + description: "Dashboard ID" + required: true + dashboard: + description: "Dashboard content object with title, widgets, layout, tags, variables" + required: true + signoz_delete_dashboard: + description: "Delete a dashboard by ID." + parameters: + id: + description: "Dashboard ID" + required: true + signoz_list_alerts: + description: "List all alert rules including their current state (firing, inactive, pending). Start here for alert management and on-call monitoring." + parameters: {} + signoz_get_alert: + description: "Get a specific alert rule by ID with full configuration and current state. Use after list_alerts." + parameters: + id: + description: "Alert rule ID" + required: true + signoz_create_alert: + description: "Create a new alert rule. Requires a full alert rule definition JSON body." + parameters: + rule: + description: "Full alert rule definition JSON object" + required: true + signoz_update_alert: + description: "Update an existing alert rule. Send the full rule object (get it first with get_alert)." + parameters: + id: + description: "Alert rule ID" + required: true + rule: + description: "Full alert rule definition JSON object" + required: true + signoz_delete_alert: + description: "Delete an alert rule by ID." + parameters: + id: + description: "Alert rule ID" + required: true + signoz_list_saved_views: + description: "List all saved explorer views for logs and traces. Use to find pre-configured query views." + parameters: {} + signoz_get_saved_view: + description: "Get a specific saved view by ID with full query configuration. Use after list_saved_views." + parameters: + view_id: + description: "Saved view ID" + required: true + signoz_create_saved_view: + description: "Create a new saved explorer view with query configuration." + parameters: + view: + description: "Saved view definition JSON object" + required: true + signoz_update_saved_view: + description: "Update an existing saved view." + parameters: + view_id: + description: "Saved view ID" + required: true + view: + description: "Updated saved view definition JSON object" + required: true + signoz_delete_saved_view: + description: "Delete a saved view by ID." + parameters: + view_id: + description: "Saved view ID" + required: true + signoz_list_channels: + description: "List all configured notification channels (Slack, email, PagerDuty, webhooks) for alerts." + parameters: {} + signoz_get_version: + description: "Get SigNoz server version, edition (community/enterprise), and setup status." + parameters: {} diff --git a/integrations/slack/tools.go b/integrations/slack/tools.go index e6dd4743..e98a7e54 100644 --- a/integrations/slack/tools.go +++ b/integrations/slack/tools.go @@ -1,449 +1,12 @@ package slack -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -const teamIDDesc = "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + mcp "github.com/daltoniam/switchboard" +) -var tools = []mcp.ToolDefinition{ - // --- Token Management --- - { - Name: mcp.ToolName("slack_token_status"), - Description: "Check token health for all workspaces: type (OAuth vs browser session), age, auto-refresh status, and source.", - Parameters: map[string]string{}, - }, - { - Name: mcp.ToolName("slack_refresh_tokens"), - Description: "Force refresh browser session tokens. Tries cookie-based HTTP refresh first (works on all platforms), then Chrome LevelDB extraction (macOS). OAuth tokens (xoxp-) don't need refresh.", - Parameters: map[string]string{ - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_list_workspaces"), - Description: "List all configured Slack workspaces with team IDs and names. Use to find the team_id for a specific workspace.", - Parameters: map[string]string{}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Conversations --- - { - Name: mcp.ToolName("slack_list_conversations"), - Description: "Start here to discover channels. List channels and DMs in the workspace. Filter by type (public_channel, private_channel, im, mpim). Returns channel IDs needed by most other Slack tools.", - Parameters: map[string]string{ - "types": "Comma-separated types: public_channel, private_channel, im, mpim (default: public_channel,private_channel)", - "limit": "Max results per page (default 100, max 1000)", - "cursor": "Pagination cursor from previous response", - "exclude_archived": "Exclude archived channels (default true)", - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_get_conversation_info"), - Description: "Get detailed information about a specific channel or DM, including topic, purpose, and member count. Use after list_conversations to inspect a channel.", - Parameters: map[string]string{ - "channel_id": "Channel or DM ID", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_conversations_history"), - Description: "Start here to read channel messages. Returns messages in reverse chronological order. Requires channel ID (C...), not channel name. Use list_conversations to find IDs.", - Parameters: map[string]string{ - "channel_id": "Channel or DM ID", - "limit": "Number of messages to fetch (default 50, max 100)", - "oldest": "Unix timestamp — get messages after this time", - "latest": "Unix timestamp — get messages before this time", - "cursor": "Pagination cursor from previous response", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_get_thread"), - Description: "Get all replies in a message thread. Use after conversations_history to read full thread replies. Requires channel ID and thread_ts from conversations_history.", - Parameters: map[string]string{ - "channel_id": "Channel or DM ID", - "thread_ts": "Thread parent message timestamp", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "thread_ts"}, - }, - { - Name: mcp.ToolName("slack_create_conversation"), - Description: "Create a new channel.", - Parameters: map[string]string{ - "name": "Channel name (lowercase, no spaces, max 80 chars)", - "is_private": "Create as private channel (default false)", - "team_id": teamIDDesc, - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("slack_archive_conversation"), - Description: "Archive a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID to archive", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_invite_to_conversation"), - Description: "Invite users to a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "user_ids": "Comma-separated user IDs to invite", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "user_ids"}, - }, - { - Name: mcp.ToolName("slack_kick_from_conversation"), - Description: "Remove a user from a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "user_id": "User ID to remove", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "user_id"}, - }, - { - Name: mcp.ToolName("slack_set_conversation_topic"), - Description: "Set the topic of a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "topic": "New topic text", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "topic"}, - }, - { - Name: mcp.ToolName("slack_set_conversation_purpose"), - Description: "Set the purpose of a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "purpose": "New purpose text", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "purpose"}, - }, - { - Name: mcp.ToolName("slack_join_conversation"), - Description: "Join a public channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID to join", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_leave_conversation"), - Description: "Leave a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID to leave", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_rename_conversation"), - Description: "Rename a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "name": "New channel name", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "name"}, - }, - - // --- Messages --- - { - Name: mcp.ToolName("slack_send_message"), - Description: "Send (post) a message to a channel or DM. Requires channel ID (C...), not channel name. Supports Slack mrkdwn formatting and threads.", - Parameters: map[string]string{ - "channel_id": "Channel or DM ID to send to", - "text": "Message text (supports Slack mrkdwn)", - "thread_ts": "Thread timestamp to reply in a thread (optional)", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "text"}, - }, - { - Name: mcp.ToolName("slack_update_message"), - Description: "Update an existing message.", - Parameters: map[string]string{ - "channel_id": "Channel ID where the message is", - "ts": "Timestamp of the message to update", - "text": "New message text", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts", "text"}, - }, - { - Name: mcp.ToolName("slack_delete_message"), - Description: "Delete a message from a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Timestamp of the message to delete", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts"}, - }, - { - Name: mcp.ToolName("slack_search_messages"), - Description: "Start here to find messages. Search across the entire Slack workspace. Supports Slack search syntax: from:@user, in:#channel, has:link, before:2024-01-01, after:2024-01-01.", - Parameters: map[string]string{ - "query": "Search query (supports Slack search modifiers)", - "count": "Number of results (default 20, max 100)", - "team_id": teamIDDesc, - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("slack_add_reaction"), - Description: "Add an emoji reaction to a message.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Message timestamp", - "emoji": "Emoji name without colons (e.g., thumbsup)", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts", "emoji"}, - }, - { - Name: mcp.ToolName("slack_remove_reaction"), - Description: "Remove an emoji reaction from a message.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Message timestamp", - "emoji": "Emoji name without colons", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts", "emoji"}, - }, - { - Name: mcp.ToolName("slack_get_reactions"), - Description: "Get all reactions on a message.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Message timestamp", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts"}, - }, - { - Name: mcp.ToolName("slack_add_pin"), - Description: "Pin a message in a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Message timestamp to pin", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts"}, - }, - { - Name: mcp.ToolName("slack_remove_pin"), - Description: "Remove a pinned message.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "ts": "Message timestamp to unpin", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "ts"}, - }, - { - Name: mcp.ToolName("slack_list_pins"), - Description: "List all pinned items in a channel. Use to find important messages and references pinned by the team.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_schedule_message"), - Description: "Schedule a message to be sent at a specific time.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "text": "Message text", - "post_at": "Unix timestamp for when to send the message", - "thread_ts": "Thread timestamp to reply in a thread (optional)", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "text", "post_at"}, - }, - - // --- Users --- - { - Name: mcp.ToolName("slack_list_users"), - Description: "Start here to find users. List all users in the workspace with display names, emails, and IDs. Supports pagination for large workspaces.", - Parameters: map[string]string{ - "limit": "Max users per page (default 200, max 1000)", - "cursor": "Pagination cursor", - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_get_user_info"), - Description: "Get detailed profile for a single user including status, timezone, and admin status. Use after list_users when you need full details for a specific person.", - Parameters: map[string]string{ - "user_id": "Slack user ID", - "team_id": teamIDDesc, - }, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("slack_get_user_presence"), - Description: "Get a user's current presence status (active/away). Use after list_users to check if someone is online.", - Parameters: map[string]string{ - "user_id": "Slack user ID", - "team_id": teamIDDesc, - }, - Required: []string{"user_id"}, - }, - { - Name: mcp.ToolName("slack_list_user_groups"), - Description: "List all user groups (handles like @engineering) in the workspace. Use to find group IDs and membership.", - Parameters: map[string]string{ - "include_users": "Include list of member user IDs (default false)", - "include_disabled": "Include disabled user groups (default false)", - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_get_user_group"), - Description: "Get members of a specific user group.", - Parameters: map[string]string{ - "usergroup_id": "User group ID", - "team_id": teamIDDesc, - }, - Required: []string{"usergroup_id"}, - }, - - // --- Extras --- - { - Name: mcp.ToolName("slack_auth_test"), - Description: "Test authentication and get current user/workspace info. Use to verify credentials and find your own user ID.", - Parameters: map[string]string{ - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_team_info"), - Description: "Get information about the workspace/team.", - Parameters: map[string]string{ - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_upload_file"), - Description: "Upload a text file or snippet to a channel.", - Parameters: map[string]string{ - "channels": "Comma-separated channel IDs to share the file in", - "content": "Text content of the file", - "filename": "Filename (e.g., report.txt)", - "title": "Title for the file", - "filetype": "File type identifier (e.g., text, python, javascript)", - "initial_comment": "Message to include with the file", - "thread_ts": "Thread timestamp to upload into a thread (optional)", - "team_id": teamIDDesc, - }, - Required: []string{"channels", "content", "filename"}, - }, - { - Name: mcp.ToolName("slack_list_files"), - Description: "List files shared in the workspace. Use to find documents, images, and snippets. Filter by channel, user, or type.", - Parameters: map[string]string{ - "channel_id": "Filter by channel ID (optional)", - "user_id": "Filter by user ID (optional)", - "types": "Filter by file type: spaces, snippets, images, gdocs, zips, pdfs (optional)", - "count": "Number of files to return (default 20, max 100)", - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_delete_file"), - Description: "Delete a file.", - Parameters: map[string]string{ - "file_id": "File ID to delete", - "team_id": teamIDDesc, - }, - Required: []string{"file_id"}, - }, - { - Name: mcp.ToolName("slack_list_emoji"), - Description: "List all custom emoji in the workspace.", - Parameters: map[string]string{ - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_set_status"), - Description: "Set the authenticated user's status.", - Parameters: map[string]string{ - "status_text": "Status text", - "status_emoji": "Status emoji (e.g., :house_with_garden:)", - "status_expiration": "Unix timestamp when status expires (0 for no expiration)", - "team_id": teamIDDesc, - }, - Required: []string{"status_text"}, - }, - { - Name: mcp.ToolName("slack_list_bookmarks"), - Description: "List bookmarks in a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id"}, - }, - { - Name: mcp.ToolName("slack_add_bookmark"), - Description: "Add a bookmark to a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "title": "Bookmark title", - "link": "URL to bookmark", - "emoji": "Emoji for the bookmark (optional)", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "title", "link"}, - }, - { - Name: mcp.ToolName("slack_remove_bookmark"), - Description: "Remove a bookmark from a channel.", - Parameters: map[string]string{ - "channel_id": "Channel ID", - "bookmark_id": "Bookmark ID to remove", - "team_id": teamIDDesc, - }, - Required: []string{"channel_id", "bookmark_id"}, - }, - { - Name: mcp.ToolName("slack_add_reminder"), - Description: "Create a reminder. Time can be natural language (e.g., 'in 15 minutes', 'tomorrow at 9am').", - Parameters: map[string]string{ - "text": "Reminder text", - "time": "When to remind (natural language or Unix timestamp)", - "user": "User ID to remind (defaults to authenticated user)", - "team_id": teamIDDesc, - }, - Required: []string{"text", "time"}, - }, - { - Name: mcp.ToolName("slack_list_reminders"), - Description: "List all reminders for the authenticated user.", - Parameters: map[string]string{ - "team_id": teamIDDesc, - }, - }, - { - Name: mcp.ToolName("slack_delete_reminder"), - Description: "Delete a reminder.", - Parameters: map[string]string{ - "reminder_id": "Reminder ID to delete", - "team_id": teamIDDesc, - }, - Required: []string{"reminder_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/slack/tools.yaml b/integrations/slack/tools.yaml new file mode 100644 index 00000000..2bbf83f4 --- /dev/null +++ b/integrations/slack/tools.yaml @@ -0,0 +1,500 @@ +version: 1 +tools: + slack_token_status: + description: "Check token health for all workspaces: type (OAuth vs browser session), age, auto-refresh status, and source." + parameters: {} + + slack_refresh_tokens: + description: "Force refresh browser session tokens. Tries cookie-based HTTP refresh first (works on all platforms), then Chrome LevelDB extraction (macOS). OAuth tokens (xoxp-) don't need refresh." + parameters: + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_workspaces: + description: "List all configured Slack workspaces with team IDs and names. Use to find the team_id for a specific workspace." + parameters: {} + + slack_list_conversations: + description: "Start here to discover channels. List channels and DMs in the workspace. Filter by type (public_channel, private_channel, im, mpim). Returns channel IDs needed by most other Slack tools." + parameters: + types: + description: "Comma-separated types: public_channel, private_channel, im, mpim (default: public_channel,private_channel)" + limit: + description: "Max results per page (default 100, max 1000)" + cursor: + description: "Pagination cursor from previous response" + exclude_archived: + description: "Exclude archived channels (default true)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_conversation_info: + description: "Get detailed information about a specific channel or DM, including topic, purpose, and member count. Use after list_conversations to inspect a channel." + parameters: + channel_id: + description: "Channel or DM ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_conversations_history: + description: "Start here to read channel messages. Returns messages in reverse chronological order. Requires channel ID (C...), not channel name. Use list_conversations to find IDs." + parameters: + channel_id: + description: "Channel or DM ID" + required: true + limit: + description: "Number of messages to fetch (default 50, max 100)" + oldest: + description: "Unix timestamp — get messages after this time" + latest: + description: "Unix timestamp — get messages before this time" + cursor: + description: "Pagination cursor from previous response" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_thread: + description: "Get all replies in a message thread. Use after conversations_history to read full thread replies. Requires channel ID and thread_ts from conversations_history." + parameters: + channel_id: + description: "Channel or DM ID" + required: true + thread_ts: + description: "Thread parent message timestamp" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_create_conversation: + description: "Create a new channel." + parameters: + name: + description: "Channel name (lowercase, no spaces, max 80 chars)" + required: true + is_private: + description: "Create as private channel (default false)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_archive_conversation: + description: "Archive a channel." + parameters: + channel_id: + description: "Channel ID to archive" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_invite_to_conversation: + description: "Invite users to a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + user_ids: + description: "Comma-separated user IDs to invite" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_kick_from_conversation: + description: "Remove a user from a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + user_id: + description: "User ID to remove" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_set_conversation_topic: + description: "Set the topic of a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + topic: + description: "New topic text" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_set_conversation_purpose: + description: "Set the purpose of a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + purpose: + description: "New purpose text" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_join_conversation: + description: "Join a public channel." + parameters: + channel_id: + description: "Channel ID to join" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_leave_conversation: + description: "Leave a channel." + parameters: + channel_id: + description: "Channel ID to leave" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_rename_conversation: + description: "Rename a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + name: + description: "New channel name" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_send_message: + description: "Send (post) a message to a channel or DM. Requires channel ID (C...), not channel name. Supports Slack mrkdwn formatting and threads." + parameters: + channel_id: + description: "Channel or DM ID to send to" + required: true + text: + description: "Message text (supports Slack mrkdwn)" + required: true + thread_ts: + description: "Thread timestamp to reply in a thread (optional)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_update_message: + description: "Update an existing message." + parameters: + channel_id: + description: "Channel ID where the message is" + required: true + ts: + description: "Timestamp of the message to update" + required: true + text: + description: "New message text" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_delete_message: + description: "Delete a message from a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Timestamp of the message to delete" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_search_messages: + description: "Start here to find messages. Search across the entire Slack workspace. Supports Slack search syntax: from:@user, in:#channel, has:link, before:2024-01-01, after:2024-01-01." + parameters: + query: + description: "Search query (supports Slack search modifiers)" + required: true + count: + description: "Number of results (default 20, max 100)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_add_reaction: + description: "Add an emoji reaction to a message." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Message timestamp" + required: true + emoji: + description: "Emoji name without colons (e.g., thumbsup)" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_remove_reaction: + description: "Remove an emoji reaction from a message." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Message timestamp" + required: true + emoji: + description: "Emoji name without colons" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_reactions: + description: "Get all reactions on a message." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Message timestamp" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_add_pin: + description: "Pin a message in a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Message timestamp to pin" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_remove_pin: + description: "Remove a pinned message." + parameters: + channel_id: + description: "Channel ID" + required: true + ts: + description: "Message timestamp to unpin" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_pins: + description: "List all pinned items in a channel. Use to find important messages and references pinned by the team." + parameters: + channel_id: + description: "Channel ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_schedule_message: + description: "Schedule a message to be sent at a specific time." + parameters: + channel_id: + description: "Channel ID" + required: true + text: + description: "Message text" + required: true + post_at: + description: "Unix timestamp for when to send the message" + required: true + thread_ts: + description: "Thread timestamp to reply in a thread (optional)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_users: + description: "Start here to find users. List all users in the workspace with display names, emails, and IDs. Supports pagination for large workspaces." + parameters: + limit: + description: "Max users per page (default 200, max 1000)" + cursor: + description: "Pagination cursor" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_user_info: + description: "Get detailed profile for a single user including status, timezone, and admin status. Use after list_users when you need full details for a specific person." + parameters: + user_id: + description: "Slack user ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_user_presence: + description: "Get a user's current presence status (active/away). Use after list_users to check if someone is online." + parameters: + user_id: + description: "Slack user ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_user_groups: + description: "List all user groups (handles like @engineering) in the workspace. Use to find group IDs and membership." + parameters: + include_users: + description: "Include list of member user IDs (default false)" + include_disabled: + description: "Include disabled user groups (default false)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_get_user_group: + description: "Get members of a specific user group." + parameters: + usergroup_id: + description: "User group ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_auth_test: + description: "Test authentication and get current user/workspace info. Use to verify credentials and find your own user ID." + parameters: + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_team_info: + description: "Get information about the workspace/team." + parameters: + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_upload_file: + description: "Upload a text file or snippet to a channel." + parameters: + channels: + description: "Comma-separated channel IDs to share the file in" + required: true + content: + description: "Text content of the file" + required: true + filename: + description: "Filename (e.g., report.txt)" + required: true + title: + description: "Title for the file" + filetype: + description: "File type identifier (e.g., text, python, javascript)" + initial_comment: + description: "Message to include with the file" + thread_ts: + description: "Thread timestamp to upload into a thread (optional)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_files: + description: "List files shared in the workspace. Use to find documents, images, and snippets. Filter by channel, user, or type." + parameters: + channel_id: + description: "Filter by channel ID (optional)" + user_id: + description: "Filter by user ID (optional)" + types: + description: "Filter by file type: spaces, snippets, images, gdocs, zips, pdfs (optional)" + count: + description: "Number of files to return (default 20, max 100)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_delete_file: + description: "Delete a file." + parameters: + file_id: + description: "File ID to delete" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_emoji: + description: "List all custom emoji in the workspace." + parameters: + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_set_status: + description: "Set the authenticated user's status." + parameters: + status_text: + description: "Status text" + required: true + status_emoji: + description: "Status emoji (e.g., :house_with_garden:)" + status_expiration: + description: "Unix timestamp when status expires (0 for no expiration)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_bookmarks: + description: "List bookmarks in a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_add_bookmark: + description: "Add a bookmark to a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + title: + description: "Bookmark title" + required: true + link: + description: "URL to bookmark" + required: true + emoji: + description: "Emoji for the bookmark (optional)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_remove_bookmark: + description: "Remove a bookmark from a channel." + parameters: + channel_id: + description: "Channel ID" + required: true + bookmark_id: + description: "Bookmark ID to remove" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_add_reminder: + description: "Create a reminder. Time can be natural language (e.g., 'in 15 minutes', 'tomorrow at 9am')." + parameters: + text: + description: "Reminder text" + required: true + time: + description: "When to remind (natural language or Unix timestamp)" + required: true + user: + description: "User ID to remind (defaults to authenticated user)" + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_list_reminders: + description: "List all reminders for the authenticated user." + parameters: + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + + slack_delete_reminder: + description: "Delete a reminder." + parameters: + reminder_id: + description: "Reminder ID to delete" + required: true + team_id: + description: "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." diff --git a/integrations/snowflake/tools.go b/integrations/snowflake/tools.go index 0f2f6328..b64ed505 100644 --- a/integrations/snowflake/tools.go +++ b/integrations/snowflake/tools.go @@ -1,192 +1,15 @@ package snowflake -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // --- Queries --- - { - Name: mcp.ToolName("snowflake_execute_query"), - Description: "Execute a SQL query against a Snowflake data warehouse and return results as JSON rows. Supports SELECT, SHOW, DESCRIBE, DDL, and DML statements", - Parameters: map[string]string{ - "query": "SQL statement to execute", - "database": "Database context (overrides configured default)", - "schema": "Schema context (overrides configured default)", - "warehouse": "Warehouse to use (overrides configured default)", - "role": "Role to use (overrides configured default)", - "timeout": "Query timeout in seconds (default: 60)", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("snowflake_get_query_status"), - Description: "Check the status of an async Snowflake query and retrieve results when complete. Use the statement handle returned from snowflake_execute_query", - Parameters: map[string]string{ - "statement_handle": "UUID statement handle from a previous query submission", - "partition": "Partition number to fetch for large result sets (0-based)", - }, - Required: []string{"statement_handle"}, - }, - { - Name: mcp.ToolName("snowflake_cancel_query"), - Description: "Cancel a running Snowflake query by its statement handle", - Parameters: map[string]string{ - "statement_handle": "UUID statement handle of the query to cancel", - }, - Required: []string{"statement_handle"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // --- Schema Discovery --- - { - Name: mcp.ToolName("snowflake_list_databases"), - Description: "List all databases accessible in the Snowflake account. Start here for schema discovery.", - Parameters: map[string]string{ - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_list_schemas"), - Description: "List all schemas in a Snowflake database", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_list_tables"), - Description: "List tables in a Snowflake database/schema with row counts and sizes", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_list_views"), - Description: "List views in a Snowflake database/schema", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_describe_table"), - Description: "Describe a table's columns with names, types, and constraints in Snowflake", - Parameters: map[string]string{ - "table": "Table name", - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - Required: []string{"table"}, - }, - { - Name: mcp.ToolName("snowflake_show_create_table"), - Description: "Show the DDL CREATE statement for a Snowflake table", - Parameters: map[string]string{ - "table": "Table name", - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - Required: []string{"table"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // --- Warehouse & Compute --- - { - Name: mcp.ToolName("snowflake_list_warehouses"), - Description: "List all warehouses in the Snowflake account with state, size, and cluster info", - Parameters: map[string]string{ - "role": "Role to use (overrides configured default)", - }, - }, - - // --- System Info --- - { - Name: mcp.ToolName("snowflake_list_running_queries"), - Description: "List currently running and recently completed queries in Snowflake", - Parameters: map[string]string{ - "limit": "Maximum number of queries to return (default: 50)", - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_current_session"), - Description: "Get current Snowflake session info including user, role, warehouse, and database", - Parameters: map[string]string{}, - }, - - // --- Users & Roles --- - { - Name: mcp.ToolName("snowflake_list_users"), - Description: "List all users in the Snowflake account", - Parameters: map[string]string{ - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_list_roles"), - Description: "List all roles in the Snowflake account", - Parameters: map[string]string{ - "role": "Role to use (overrides configured default)", - }, - }, - - // --- Stages & Storage --- - { - Name: mcp.ToolName("snowflake_list_stages"), - Description: "List stages in a Snowflake database/schema for data loading", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - - // --- Tasks & Pipes --- - { - Name: mcp.ToolName("snowflake_list_tasks"), - Description: "List tasks (scheduled SQL jobs) in a Snowflake database/schema", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - { - Name: mcp.ToolName("snowflake_list_pipes"), - Description: "List Snowpipe definitions for continuous data ingestion", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - - // --- Streams --- - { - Name: mcp.ToolName("snowflake_list_streams"), - Description: "List streams (change data capture) in a Snowflake database/schema", - Parameters: map[string]string{ - "database": "Database name (defaults to configured database)", - "schema": "Schema name (defaults to configured schema)", - "role": "Role to use (overrides configured default)", - }, - }, - - // --- Cortex Analyst --- - { - Name: mcp.ToolName("snowflake_cortex_analyst"), - Description: "Ask a natural-language question against a Snowflake Cortex Analyst semantic layer. Returns generated SQL, an explanation, and follow-up suggestions. Use snowflake_execute_query to run the returned SQL", - Parameters: map[string]string{ - "question": "Natural-language question to ask (e.g. 'What were our top 10 products by revenue last quarter?')", - "semantic_view": "Fully qualified semantic view name (overrides configured default)", - "semantic_model_file": "Stage path to a semantic model YAML (e.g. @MY_DB.MY_SCHEMA.MY_STAGE/model.yaml)", - "semantic_model": "Inline semantic model YAML (alternative to semantic_model_file and semantic_view)", - }, - Required: []string{"question"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) var dispatch = map[mcp.ToolName]handlerFunc{ // Queries diff --git a/integrations/snowflake/tools.yaml b/integrations/snowflake/tools.yaml new file mode 100644 index 00000000..a647ac32 --- /dev/null +++ b/integrations/snowflake/tools.yaml @@ -0,0 +1,159 @@ +version: 1 +tools: + snowflake_execute_query: + description: "Execute a SQL query against a Snowflake data warehouse and return results as JSON rows. Supports SELECT, SHOW, DESCRIBE, DDL, and DML statements" + parameters: + query: + description: "SQL statement to execute" + required: true + database: + description: "Database context (overrides configured default)" + schema: + description: "Schema context (overrides configured default)" + warehouse: + description: "Warehouse to use (overrides configured default)" + role: + description: "Role to use (overrides configured default)" + timeout: + description: "Query timeout in seconds (default: 60)" + snowflake_get_query_status: + description: "Check the status of an async Snowflake query and retrieve results when complete. Use the statement handle returned from snowflake_execute_query" + parameters: + statement_handle: + description: "UUID statement handle from a previous query submission" + required: true + partition: + description: "Partition number to fetch for large result sets (0-based)" + snowflake_cancel_query: + description: "Cancel a running Snowflake query by its statement handle" + parameters: + statement_handle: + description: "UUID statement handle of the query to cancel" + required: true + snowflake_list_databases: + description: "List all databases accessible in the Snowflake account. Start here for schema discovery." + parameters: + role: + description: "Role to use (overrides configured default)" + snowflake_list_schemas: + description: "List all schemas in a Snowflake database" + parameters: + database: + description: "Database name (defaults to configured database)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_tables: + description: "List tables in a Snowflake database/schema with row counts and sizes" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_views: + description: "List views in a Snowflake database/schema" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_describe_table: + description: "Describe a table's columns with names, types, and constraints in Snowflake" + parameters: + table: + description: "Table name" + required: true + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_show_create_table: + description: "Show the DDL CREATE statement for a Snowflake table" + parameters: + table: + description: "Table name" + required: true + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_warehouses: + description: "List all warehouses in the Snowflake account with state, size, and cluster info" + parameters: + role: + description: "Role to use (overrides configured default)" + snowflake_list_running_queries: + description: "List currently running and recently completed queries in Snowflake" + parameters: + limit: + description: "Maximum number of queries to return (default: 50)" + role: + description: "Role to use (overrides configured default)" + snowflake_current_session: + description: "Get current Snowflake session info including user, role, warehouse, and database" + parameters: {} + snowflake_list_users: + description: "List all users in the Snowflake account" + parameters: + role: + description: "Role to use (overrides configured default)" + snowflake_list_roles: + description: "List all roles in the Snowflake account" + parameters: + role: + description: "Role to use (overrides configured default)" + snowflake_list_stages: + description: "List stages in a Snowflake database/schema for data loading" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_tasks: + description: "List tasks (scheduled SQL jobs) in a Snowflake database/schema" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_pipes: + description: "List Snowpipe definitions for continuous data ingestion" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_list_streams: + description: "List streams (change data capture) in a Snowflake database/schema" + parameters: + database: + description: "Database name (defaults to configured database)" + schema: + description: "Schema name (defaults to configured schema)" + role: + description: "Role to use (overrides configured default)" + snowflake_cortex_analyst: + description: "Ask a natural-language question against a Snowflake Cortex Analyst semantic layer. Returns generated SQL, an explanation, and follow-up suggestions. Use snowflake_execute_query to run the returned SQL" + parameters: + question: + description: "Natural-language question to ask (e.g. 'What were our top 10 products by revenue last quarter?')" + required: true + semantic_view: + description: "Fully qualified semantic view name (overrides configured default)" + semantic_model_file: + description: "Stage path to a semantic model YAML (e.g. @MY_DB.MY_SCHEMA.MY_STAGE/model.yaml)" + semantic_model: + description: "Inline semantic model YAML (alternative to semantic_model_file and semantic_view)" diff --git a/integrations/stripe/tools.go b/integrations/stripe/tools.go index 41f7cbd7..45866847 100644 --- a/integrations/stripe/tools.go +++ b/integrations/stripe/tools.go @@ -1,770 +1,12 @@ package stripe -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -// Standard list-pagination params reused across many list tools. -var listParams = map[string]string{ - "limit": "Number of objects to return (1-100, default 10)", - "starting_after": "Cursor for pagination — an object ID for the next page", - "ending_before": "Cursor for pagination — an object ID for the previous page", -} + mcp "github.com/daltoniam/switchboard" +) -var tools = []mcp.ToolDefinition{ - // ── Balance ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_get_balance"), - Description: "Get the current Stripe account balance (available, pending, connect_reserved). Start here to inspect funds on the Stripe account.", - }, - { - Name: mcp.ToolName("stripe_list_balance_transactions"), - Description: "List balance transactions (charges, refunds, payouts, fees, transfers) that affected the Stripe account balance. Use for accounting reconciliation, financial audits, fee analysis, and tracking money movement on the Stripe platform.", - Parameters: mergeParams(listParams, map[string]string{ - "type": "Filter by type (charge, refund, payout, transfer, adjustment, fee, etc.)", - "currency": "Three-letter ISO currency code lowercase (e.g. usd)", - "payout": "Filter to transactions paid out in this payout ID", - "source": "Filter by source ID (charge, refund, etc.)", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_balance_transaction"), - Description: "Retrieve (get) a single balance transaction by ID.", - Parameters: map[string]string{"id": "Balance transaction ID (e.g. txn_...)"}, - Required: []string{"id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Customers ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_customers"), - Description: "List Stripe customers. Use for browsing payers, finding accounts by email, exporting customer rosters, or paginating the customer directory.", - Parameters: mergeParams(listParams, map[string]string{ - "email": "Filter by exact email match", - "created": "Filter by created timestamp (Unix epoch seconds) — pass a number or use nested keys gt/gte/lt/lte for ranges", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_customer"), - Description: "Retrieve (get) a single customer by ID including their default payment method, billing address, and metadata.", - Parameters: map[string]string{"id": "Customer ID (e.g. cus_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_search_customers"), - Description: "Search Stripe customers using Stripe search query language (e.g. email:\"alice@example.com\" or metadata['plan']:\"pro\"). Returns matching customer records.", - Parameters: map[string]string{ - "query": "Stripe search query syntax", - "limit": "Number of results (1-100)", - "page": "Cursor for pagination (from prior response.next_page)", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("stripe_create_customer"), - Description: "Create a new Stripe customer record for a payer.", - Parameters: map[string]string{ - "email": "Customer email address", - "name": "Full customer name", - "phone": "Phone number", - "description": "Arbitrary description for internal use", - "metadata": "Object of key-value strings to attach (max 50 keys)", - "address": "Object with line1, line2, city, state, postal_code, country", - "shipping": "Object with name, phone, address", - }, - }, - { - Name: mcp.ToolName("stripe_update_customer"), - Description: "Update (edit) a customer's profile, contact info, default payment method, or metadata.", - Parameters: map[string]string{ - "id": "Customer ID", - "email": "New email", - "name": "New name", - "phone": "New phone", - "description": "New description", - "metadata": "Object to merge into existing metadata (set key to empty string to clear it)", - "default_source": "Default payment source ID", - "invoice_settings": "Object with default_payment_method, custom_fields, footer", - "address": "Billing address object", - "shipping": "Shipping address object", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_delete_customer"), - Description: "Delete (remove) a customer permanently. Cancels active subscriptions and disassociates payment methods.", - Parameters: map[string]string{"id": "Customer ID"}, - Required: []string{"id"}, - }, - - // ── Charges ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_charges"), - Description: "List charges (card and bank transactions) processed on the Stripe account. Use for transaction history, sales reports, revenue analytics, and finding individual successful or failed payments.", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "payment_intent": "Filter by payment intent ID", - "transfer_group": "Filter by transfer group", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_charge"), - Description: "Retrieve (get) a single charge by ID with full details (amount, currency, status, customer, payment_method, refunds, dispute, outcome).", - Parameters: map[string]string{"id": "Charge ID (e.g. ch_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_search_charges"), - Description: "Search charges using Stripe search query language (e.g. amount>1000 AND status:\"succeeded\").", - Parameters: map[string]string{ - "query": "Stripe search query", - "limit": "Number of results (1-100)", - "page": "Cursor for pagination", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("stripe_capture_charge"), - Description: "Capture a previously authorized but uncaptured charge (manual capture flow).", - Parameters: map[string]string{ - "id": "Charge ID", - "amount": "Optional amount to capture in smallest currency unit (cents). Defaults to full authorized amount.", - }, - Required: []string{"id"}, - }, - - // ── Payment Intents ────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_payment_intents"), - Description: "List PaymentIntents — the modern payment flow object representing intent to collect from a customer. Use for monitoring in-progress, succeeded, requires_action, or failed payments.", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_payment_intent"), - Description: "Retrieve (get) a single PaymentIntent by ID with status, next_action, latest_charge, and client_secret.", - Parameters: map[string]string{"id": "PaymentIntent ID (e.g. pi_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_payment_intent"), - Description: "Create a new PaymentIntent to charge a customer. Amounts are in the smallest currency unit (e.g. cents for USD).", - Parameters: map[string]string{ - "amount": "Amount in smallest currency unit (e.g. 1099 = $10.99)", - "currency": "Three-letter ISO currency lowercase (e.g. usd)", - "customer": "Customer ID to associate with this payment", - "payment_method": "Payment method ID to charge", - "payment_method_types": "Array of payment method types (e.g. [\"card\"])", - "description": "Arbitrary description shown to the customer", - "receipt_email": "Email address to send receipt to", - "statement_descriptor": "Up to 22 chars shown on the customer's statement", - "capture_method": "automatic or manual", - "confirm": "If true, confirm the PaymentIntent in the same request (boolean)", - "off_session": "Boolean indicating customer is not present", - "metadata": "Object of key-value strings", - }, - Required: []string{"amount", "currency"}, - }, - { - Name: mcp.ToolName("stripe_update_payment_intent"), - Description: "Update (edit) a PaymentIntent before it is confirmed.", - Parameters: map[string]string{ - "id": "PaymentIntent ID", - "amount": "New amount in smallest currency unit", - "currency": "Currency code", - "customer": "Customer ID", - "description": "New description", - "metadata": "Metadata object", - "payment_method": "Payment method ID", - "receipt_email": "Receipt email address", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_confirm_payment_intent"), - Description: "Confirm a PaymentIntent to attempt to collect payment.", - Parameters: map[string]string{ - "id": "PaymentIntent ID", - "payment_method": "Payment method ID to attach for confirmation", - "return_url": "URL to redirect after 3DS/redirect-based authentication", - "off_session": "Boolean — confirming on behalf of an absent customer", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_cancel_payment_intent"), - Description: "Cancel a PaymentIntent that is in a cancelable state (requires_payment_method, requires_capture, requires_confirmation, requires_action, processing).", - Parameters: map[string]string{ - "id": "PaymentIntent ID", - "cancellation_reason": "duplicate, fraudulent, requested_by_customer, abandoned", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_search_payment_intents"), - Description: "Search PaymentIntents with Stripe search query (e.g. status:\"requires_action\" AND amount>5000).", - Parameters: map[string]string{ - "query": "Stripe search query", - "limit": "Number of results", - "page": "Cursor for pagination", - }, - Required: []string{"query"}, - }, - - // ── Refunds ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_refunds"), - Description: "List refunds processed on the Stripe account. Use for reviewing returned money, refund audits, and tracking refund status.", - Parameters: mergeParams(listParams, map[string]string{ - "charge": "Filter by charge ID", - "payment_intent": "Filter by payment intent ID", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_refund"), - Description: "Retrieve (get) a single refund by ID.", - Parameters: map[string]string{"id": "Refund ID (e.g. re_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_refund"), - Description: "Create a refund (full or partial) for a charge or PaymentIntent. Returns money to the customer.", - Parameters: map[string]string{ - "charge": "Charge ID to refund (one of charge or payment_intent required)", - "payment_intent": "PaymentIntent ID to refund", - "amount": "Amount to refund in smallest currency unit (defaults to full)", - "reason": "duplicate, fraudulent, or requested_by_customer", - "refund_application_fee": "Boolean — whether to refund the application fee", - "reverse_transfer": "Boolean — reverse the transfer to a connected account", - "metadata": "Metadata object", - }, - }, - { - Name: mcp.ToolName("stripe_update_refund"), - Description: "Update (edit) a refund's metadata.", - Parameters: map[string]string{ - "id": "Refund ID", - "metadata": "Metadata object to merge", - }, - Required: []string{"id"}, - }, - - // ── Disputes ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_disputes"), - Description: "List chargeback disputes filed against charges on the Stripe account. Use for fraud monitoring, chargeback response workflows, and dispute analytics.", - Parameters: mergeParams(listParams, map[string]string{ - "charge": "Filter by charge ID", - "payment_intent": "Filter by payment intent ID", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_dispute"), - Description: "Retrieve (get) a single dispute by ID including evidence and status.", - Parameters: map[string]string{"id": "Dispute ID (e.g. dp_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_update_dispute"), - Description: "Update (edit) a dispute to submit evidence for chargeback response.", - Parameters: map[string]string{ - "id": "Dispute ID", - "evidence": "Evidence object (e.g. customer_communication, receipt, service_documentation, shipping_documentation, etc.)", - "submit": "Boolean — submit evidence immediately", - "metadata": "Metadata object", - }, - Required: []string{"id"}, - }, - - // ── Payouts ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_payouts"), - Description: "List payouts (transfers from the Stripe balance to a bank account). Use for cash-out tracking, settlement reconciliation, and finance reports.", - Parameters: mergeParams(listParams, map[string]string{ - "status": "Filter by status (paid, pending, in_transit, canceled, failed)", - "destination": "Filter by bank account or card destination ID", - "arrival_date": "Filter by arrival_date timestamp", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_payout"), - Description: "Retrieve (get) a single payout by ID.", - Parameters: map[string]string{"id": "Payout ID (e.g. po_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_payout"), - Description: "Create a manual payout from the Stripe balance to the default bank account.", - Parameters: map[string]string{ - "amount": "Amount in smallest currency unit", - "currency": "Three-letter ISO currency lowercase", - "description": "Description", - "method": "standard or instant", - "destination": "Bank account or debit card ID (optional override)", - "metadata": "Metadata object", - }, - Required: []string{"amount", "currency"}, - }, - - // ── Subscriptions ──────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_subscriptions"), - Description: "List recurring billing subscriptions. Use for MRR/ARR reports, churn analysis, active customer counts, and finding subscriptions in trial, past_due, or canceled state.", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "price": "Filter by price ID", - "status": "Filter by status (active, past_due, unpaid, canceled, incomplete, incomplete_expired, trialing, all)", - "collection_method": "charge_automatically or send_invoice", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_subscription"), - Description: "Retrieve (get) a single subscription by ID including items, current period, and billing cycle.", - Parameters: map[string]string{"id": "Subscription ID (e.g. sub_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_search_subscriptions"), - Description: "Search subscriptions using Stripe search query (e.g. status:\"trialing\" AND created>1700000000).", - Parameters: map[string]string{ - "query": "Stripe search query", - "limit": "Number of results", - "page": "Cursor for pagination", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("stripe_create_subscription"), - Description: "Create a new subscription that recurringly bills a customer for one or more prices.", - Parameters: map[string]string{ - "customer": "Customer ID to subscribe (required)", - "items": "Array of subscription items, each with a 'price' ID and optional 'quantity'", - "default_payment_method": "Payment method ID to use for invoices", - "trial_period_days": "Number of days for free trial", - "trial_end": "Unix timestamp when trial ends (or 'now')", - "collection_method": "charge_automatically or send_invoice", - "days_until_due": "Days until invoice is due (only for send_invoice)", - "coupon": "Coupon ID to apply", - "metadata": "Metadata object", - }, - Required: []string{"customer", "items"}, - }, - { - Name: mcp.ToolName("stripe_update_subscription"), - Description: "Update (edit) a subscription's items, prices, quantities, billing settings, or metadata.", - Parameters: map[string]string{ - "id": "Subscription ID", - "items": "Updated items array — each item may include 'id' (existing item), 'price', 'quantity', or 'deleted':true", - "cancel_at_period_end": "Boolean — cancel at end of current period", - "default_payment_method": "Payment method ID", - "proration_behavior": "create_prorations, none, or always_invoice", - "trial_end": "Unix timestamp or 'now'", - "metadata": "Metadata object", - "pause_collection": "Object with behavior (keep_as_draft, mark_uncollectible, void) and resumes_at", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_cancel_subscription"), - Description: "Cancel a subscription immediately (or at period end via stripe_update_subscription with cancel_at_period_end).", - Parameters: map[string]string{ - "id": "Subscription ID", - "invoice_now": "Boolean — generate a final invoice for unbilled usage", - "prorate": "Boolean — credit prorated unused time", - "cancellation_details": "Object with comment and feedback", - }, - Required: []string{"id"}, - }, - - // ── Subscription Items ─────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_subscription_items"), - Description: "List items (line items) belonging to a subscription.", - Parameters: mergeParams(listParams, map[string]string{ - "subscription": "Subscription ID", - }), - Required: []string{"subscription"}, - }, - { - Name: mcp.ToolName("stripe_retrieve_subscription_item"), - Description: "Retrieve (get) a single subscription item by ID.", - Parameters: map[string]string{"id": "Subscription item ID (e.g. si_...)"}, - Required: []string{"id"}, - }, - - // ── Products ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_products"), - Description: "List products (goods or services sold on Stripe). Use for product catalog browsing and finding SKUs.", - Parameters: mergeParams(listParams, map[string]string{ - "active": "Filter by active (true/false)", - "ids": "Comma-separated product IDs", - "shippable": "Filter shippable goods (true/false)", - "url": "Filter by URL", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_product"), - Description: "Retrieve (get) a single product by ID.", - Parameters: map[string]string{"id": "Product ID (e.g. prod_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_product"), - Description: "Create a new product to sell.", - Parameters: map[string]string{ - "name": "Product name (required)", - "description": "Product description", - "active": "Boolean — whether the product can be used", - "default_price_data": "Object describing the default price (currency, unit_amount, recurring)", - "images": "Array of image URLs", - "metadata": "Metadata object", - "shippable": "Boolean — whether the product is shippable", - "tax_code": "Tax code ID", - "url": "Product URL", - }, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("stripe_update_product"), - Description: "Update (edit) a product's name, description, images, or metadata.", - Parameters: map[string]string{ - "id": "Product ID", - "name": "New name", - "description": "New description", - "active": "Boolean", - "default_price": "Default price ID", - "images": "Array of image URLs", - "metadata": "Metadata object", - "url": "Product URL", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_delete_product"), - Description: "Delete (remove) a product. Only succeeds if no prices reference it.", - Parameters: map[string]string{"id": "Product ID"}, - Required: []string{"id"}, - }, - - // ── Prices ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_prices"), - Description: "List prices attached to products. Each price defines how much and how often to charge.", - Parameters: mergeParams(listParams, map[string]string{ - "product": "Filter by product ID", - "active": "Filter by active", - "currency": "Filter by currency", - "type": "one_time or recurring", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_price"), - Description: "Retrieve (get) a single price by ID.", - Parameters: map[string]string{"id": "Price ID (e.g. price_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_price"), - Description: "Create a new price for a product (one-time or recurring).", - Parameters: map[string]string{ - "product": "Product ID (required if product_data not given)", - "product_data": "Inline product object with name to create alongside the price", - "currency": "Three-letter ISO currency lowercase (required)", - "unit_amount": "Price in smallest currency unit (required for fixed pricing)", - "recurring": "Object with interval (day/week/month/year) and interval_count", - "nickname": "Internal display name", - "active": "Boolean", - "billing_scheme": "per_unit or tiered", - "tiers": "Array of tier objects (for tiered pricing)", - "tiers_mode": "graduated or volume", - "metadata": "Metadata object", - }, - Required: []string{"currency"}, - }, - { - Name: mcp.ToolName("stripe_update_price"), - Description: "Update (edit) a price's nickname, active flag, or metadata. Note: most fields are immutable on existing prices.", - Parameters: map[string]string{ - "id": "Price ID", - "active": "Boolean", - "nickname": "Nickname", - "metadata": "Metadata object", - }, - Required: []string{"id"}, - }, - - // ── Invoices ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_invoices"), - Description: "List invoices on the Stripe account. Use for billing audits, finding past_due/open/paid invoices, and revenue reporting.", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "subscription": "Filter by subscription ID", - "status": "draft, open, paid, uncollectible, void", - "collection_method": "charge_automatically or send_invoice", - "created": "Filter by creation timestamp", - "due_date": "Filter by due_date timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_invoice"), - Description: "Retrieve (get) a single invoice by ID with line items, totals, and status.", - Parameters: map[string]string{"id": "Invoice ID (e.g. in_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_retrieve_upcoming_invoice"), - Description: "Retrieve (get) the upcoming (preview) invoice for a customer or subscription before it is finalized. Useful for previewing the next bill.", - Parameters: map[string]string{ - "customer": "Customer ID", - "subscription": "Subscription ID", - "coupon": "Coupon ID for preview", - }, - }, - { - Name: mcp.ToolName("stripe_search_invoices"), - Description: "Search invoices with Stripe search query (e.g. status:\"open\" AND total>10000).", - Parameters: map[string]string{ - "query": "Stripe search query", - "limit": "Number of results", - "page": "Cursor for pagination", - }, - Required: []string{"query"}, - }, - { - Name: mcp.ToolName("stripe_create_invoice"), - Description: "Create a draft invoice for a customer. Add invoice items separately or use auto_advance to finalize automatically.", - Parameters: map[string]string{ - "customer": "Customer ID (required)", - "auto_advance": "Boolean — finalize and attempt collection automatically", - "collection_method": "charge_automatically or send_invoice", - "days_until_due": "Days until due (for send_invoice)", - "description": "Description", - "subscription": "Subscription ID to associate", - "metadata": "Metadata object", - }, - Required: []string{"customer"}, - }, - { - Name: mcp.ToolName("stripe_finalize_invoice"), - Description: "Finalize a draft invoice — locks line items and generates the final amount.", - Parameters: map[string]string{ - "id": "Invoice ID", - "auto_advance": "Boolean — proceed with payment collection", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_pay_invoice"), - Description: "Pay an open invoice using a payment method or by charging the customer's default source.", - Parameters: map[string]string{ - "id": "Invoice ID", - "payment_method": "Payment method ID to charge", - "paid_out_of_band": "Boolean — mark as paid externally without collecting funds", - "off_session": "Boolean", - }, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_send_invoice"), - Description: "Email an open invoice to the customer.", - Parameters: map[string]string{"id": "Invoice ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_void_invoice"), - Description: "Void an open invoice. Cannot be undone.", - Parameters: map[string]string{"id": "Invoice ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_delete_invoice"), - Description: "Delete (remove) a draft invoice permanently. Only allowed for draft invoices.", - Parameters: map[string]string{"id": "Invoice ID"}, - Required: []string{"id"}, - }, - - // ── Invoice Items ──────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_invoice_items"), - Description: "List pending or attached invoice items (line items added to a customer's next invoice).", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "invoice": "Filter by invoice ID", - "pending": "Only return pending items (true/false)", - }), - }, - { - Name: mcp.ToolName("stripe_create_invoice_item"), - Description: "Create an invoice item that will be added to a customer's next invoice or attached to a specific invoice.", - Parameters: map[string]string{ - "customer": "Customer ID (required)", - "amount": "Amount in smallest currency unit", - "currency": "Currency code", - "description": "Line item description", - "price": "Price ID (alternative to amount+currency)", - "quantity": "Quantity (defaults to 1)", - "invoice": "Optional invoice ID to attach to", - "subscription": "Subscription ID to attach to", - "metadata": "Metadata object", - }, - Required: []string{"customer"}, - }, - - // ── Payment Methods ────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_payment_methods"), - Description: "List payment methods attached to a customer (cards, bank accounts, wallets).", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Customer ID (required)", - "type": "card, us_bank_account, sepa_debit, etc.", - }), - Required: []string{"customer"}, - }, - { - Name: mcp.ToolName("stripe_retrieve_payment_method"), - Description: "Retrieve (get) a single payment method by ID.", - Parameters: map[string]string{"id": "Payment method ID (e.g. pm_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_attach_payment_method"), - Description: "Attach a payment method to a customer for later reuse.", - Parameters: map[string]string{ - "id": "Payment method ID", - "customer": "Customer ID to attach to", - }, - Required: []string{"id", "customer"}, - }, - { - Name: mcp.ToolName("stripe_detach_payment_method"), - Description: "Detach (remove) a payment method from its customer.", - Parameters: map[string]string{"id": "Payment method ID"}, - Required: []string{"id"}, - }, - - // ── Setup Intents ──────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_setup_intents"), - Description: "List SetupIntents (objects representing intent to save a payment method for future use).", - Parameters: mergeParams(listParams, map[string]string{ - "customer": "Filter by customer ID", - "payment_method": "Filter by payment method ID", - "created": "Filter by creation timestamp", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_setup_intent"), - Description: "Retrieve (get) a single SetupIntent by ID.", - Parameters: map[string]string{"id": "SetupIntent ID (e.g. seti_...)"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_setup_intent"), - Description: "Create a SetupIntent to collect a customer's payment method for future use.", - Parameters: map[string]string{ - "customer": "Customer ID", - "payment_method": "Payment method ID", - "payment_method_types": "Array of allowed payment method types", - "usage": "on_session or off_session", - "confirm": "Boolean — confirm immediately", - "description": "Description", - "metadata": "Metadata object", - }, - }, - - // ── Coupons / Promotion Codes ──────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_coupons"), - Description: "List discount coupons defined on the Stripe account.", - Parameters: mergeParams(listParams, nil), - }, - { - Name: mcp.ToolName("stripe_retrieve_coupon"), - Description: "Retrieve (get) a single coupon by ID.", - Parameters: map[string]string{"id": "Coupon ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_create_coupon"), - Description: "Create a discount coupon that can be applied to customers, invoices, or subscriptions.", - Parameters: map[string]string{ - "id": "Optional coupon ID (auto-generated if omitted)", - "name": "Display name shown on receipts/invoices", - "percent_off": "Percentage discount (0-100). Use this or amount_off.", - "amount_off": "Fixed amount discount in smallest currency unit", - "currency": "Required when amount_off is set", - "duration": "once, repeating, or forever", - "duration_in_months": "Number of months (for duration=repeating)", - "max_redemptions": "Maximum total redemptions", - "redeem_by": "Unix timestamp coupon expires for new redemptions", - "metadata": "Metadata object", - }, - Required: []string{"duration"}, - }, - { - Name: mcp.ToolName("stripe_delete_coupon"), - Description: "Delete (remove) a coupon.", - Parameters: map[string]string{"id": "Coupon ID"}, - Required: []string{"id"}, - }, - { - Name: mcp.ToolName("stripe_list_promotion_codes"), - Description: "List customer-facing promotion codes (the code strings tied to a coupon).", - Parameters: mergeParams(listParams, map[string]string{ - "coupon": "Filter by coupon ID", - "customer": "Filter by customer ID", - "active": "Filter by active", - "code": "Filter by code string", - }), - }, - { - Name: mcp.ToolName("stripe_create_promotion_code"), - Description: "Create a customer-facing promotion code tied to a coupon.", - Parameters: map[string]string{ - "coupon": "Coupon ID (required)", - "code": "Customer-facing code (auto-generated if omitted)", - "customer": "Restrict to a specific customer ID", - "max_redemptions": "Maximum total redemptions", - "expires_at": "Unix timestamp", - "active": "Boolean", - "metadata": "Metadata object", - }, - Required: []string{"coupon"}, - }, - - // ── Events ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("stripe_list_events"), - Description: "List Stripe events (webhook event log). Use for auditing webhook history, replaying missed events, or finding when a specific object changed.", - Parameters: mergeParams(listParams, map[string]string{ - "type": "Filter by event type (e.g. charge.succeeded, invoice.paid)", - "types": "Array of event types", - "created": "Filter by creation timestamp", - "delivery_success": "Boolean — filter by webhook delivery outcome", - }), - }, - { - Name: mcp.ToolName("stripe_retrieve_event"), - Description: "Retrieve (get) a single event by ID.", - Parameters: map[string]string{"id": "Event ID (e.g. evt_...)"}, - Required: []string{"id"}, - }, -} - -// mergeParams merges base + extra into a fresh map (base takes precedence for duplicates). -func mergeParams(base, extra map[string]string) map[string]string { - out := make(map[string]string, len(base)+len(extra)) - for k, v := range extra { - out[k] = v - } - for k, v := range base { - out[k] = v - } - return out -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/stripe/tools.yaml b/integrations/stripe/tools.yaml new file mode 100644 index 00000000..19250545 --- /dev/null +++ b/integrations/stripe/tools.yaml @@ -0,0 +1,916 @@ +version: 1 +tools: + stripe_get_balance: + description: "Get the current Stripe account balance (available, pending, connect_reserved). Start here to inspect funds on the Stripe account." + stripe_list_balance_transactions: + description: "List balance transactions (charges, refunds, payouts, fees, transfers) that affected the Stripe account balance. Use for accounting reconciliation, financial audits, fee analysis, and tracking money movement on the Stripe platform." + parameters: + type: + description: "Filter by type (charge, refund, payout, transfer, adjustment, fee, etc.)" + currency: + description: "Three-letter ISO currency code lowercase (e.g. usd)" + payout: + description: "Filter to transactions paid out in this payout ID" + source: + description: "Filter by source ID (charge, refund, etc.)" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_balance_transaction: + description: "Retrieve (get) a single balance transaction by ID." + parameters: + id: + description: "Balance transaction ID (e.g. txn_...)" + required: true + stripe_list_customers: + description: "List Stripe customers. Use for browsing payers, finding accounts by email, exporting customer rosters, or paginating the customer directory." + parameters: + email: + description: "Filter by exact email match" + created: + description: "Filter by created timestamp (Unix epoch seconds) — pass a number or use nested keys gt/gte/lt/lte for ranges" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_customer: + description: "Retrieve (get) a single customer by ID including their default payment method, billing address, and metadata." + parameters: + id: + description: "Customer ID (e.g. cus_...)" + required: true + stripe_search_customers: + description: 'Search Stripe customers using Stripe search query language (e.g. email:"alice@example.com" or metadata[''plan'']:"pro"). Returns matching customer records.' + parameters: + query: + description: "Stripe search query syntax" + required: true + limit: + description: "Number of results (1-100)" + page: + description: "Cursor for pagination (from prior response.next_page)" + stripe_create_customer: + description: "Create a new Stripe customer record for a payer." + parameters: + email: + description: "Customer email address" + name: + description: "Full customer name" + phone: + description: "Phone number" + description: + description: "Arbitrary description for internal use" + metadata: + description: "Object of key-value strings to attach (max 50 keys)" + address: + description: "Object with line1, line2, city, state, postal_code, country" + shipping: + description: "Object with name, phone, address" + stripe_update_customer: + description: "Update (edit) a customer's profile, contact info, default payment method, or metadata." + parameters: + id: + description: "Customer ID" + required: true + email: + description: "New email" + name: + description: "New name" + phone: + description: "New phone" + description: + description: "New description" + metadata: + description: "Object to merge into existing metadata (set key to empty string to clear it)" + default_source: + description: "Default payment source ID" + invoice_settings: + description: "Object with default_payment_method, custom_fields, footer" + address: + description: "Billing address object" + shipping: + description: "Shipping address object" + stripe_delete_customer: + description: "Delete (remove) a customer permanently. Cancels active subscriptions and disassociates payment methods." + parameters: + id: + description: "Customer ID" + required: true + stripe_list_charges: + description: "List charges (card and bank transactions) processed on the Stripe account. Use for transaction history, sales reports, revenue analytics, and finding individual successful or failed payments." + parameters: + customer: + description: "Filter by customer ID" + payment_intent: + description: "Filter by payment intent ID" + transfer_group: + description: "Filter by transfer group" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_charge: + description: "Retrieve (get) a single charge by ID with full details (amount, currency, status, customer, payment_method, refunds, dispute, outcome)." + parameters: + id: + description: "Charge ID (e.g. ch_...)" + required: true + stripe_search_charges: + description: 'Search charges using Stripe search query language (e.g. amount>1000 AND status:"succeeded").' + parameters: + query: + description: "Stripe search query" + required: true + limit: + description: "Number of results (1-100)" + page: + description: "Cursor for pagination" + stripe_capture_charge: + description: "Capture a previously authorized but uncaptured charge (manual capture flow)." + parameters: + id: + description: "Charge ID" + required: true + amount: + description: "Optional amount to capture in smallest currency unit (cents). Defaults to full authorized amount." + stripe_list_payment_intents: + description: "List PaymentIntents — the modern payment flow object representing intent to collect from a customer. Use for monitoring in-progress, succeeded, requires_action, or failed payments." + parameters: + customer: + description: "Filter by customer ID" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_payment_intent: + description: "Retrieve (get) a single PaymentIntent by ID with status, next_action, latest_charge, and client_secret." + parameters: + id: + description: "PaymentIntent ID (e.g. pi_...)" + required: true + stripe_create_payment_intent: + description: "Create a new PaymentIntent to charge a customer. Amounts are in the smallest currency unit (e.g. cents for USD)." + parameters: + amount: + description: "Amount in smallest currency unit (e.g. 1099 = $10.99)" + required: true + currency: + description: "Three-letter ISO currency lowercase (e.g. usd)" + required: true + customer: + description: "Customer ID to associate with this payment" + payment_method: + description: "Payment method ID to charge" + payment_method_types: + description: 'Array of payment method types (e.g. ["card"])' + description: + description: "Arbitrary description shown to the customer" + receipt_email: + description: "Email address to send receipt to" + statement_descriptor: + description: "Up to 22 chars shown on the customer's statement" + capture_method: + description: "automatic or manual" + confirm: + description: "If true, confirm the PaymentIntent in the same request (boolean)" + off_session: + description: "Boolean indicating customer is not present" + metadata: + description: "Object of key-value strings" + stripe_update_payment_intent: + description: "Update (edit) a PaymentIntent before it is confirmed." + parameters: + id: + description: "PaymentIntent ID" + required: true + amount: + description: "New amount in smallest currency unit" + currency: + description: "Currency code" + customer: + description: "Customer ID" + description: + description: "New description" + metadata: + description: "Metadata object" + payment_method: + description: "Payment method ID" + receipt_email: + description: "Receipt email address" + stripe_confirm_payment_intent: + description: "Confirm a PaymentIntent to attempt to collect payment." + parameters: + id: + description: "PaymentIntent ID" + required: true + payment_method: + description: "Payment method ID to attach for confirmation" + return_url: + description: "URL to redirect after 3DS/redirect-based authentication" + off_session: + description: "Boolean — confirming on behalf of an absent customer" + stripe_cancel_payment_intent: + description: "Cancel a PaymentIntent that is in a cancelable state (requires_payment_method, requires_capture, requires_confirmation, requires_action, processing)." + parameters: + id: + description: "PaymentIntent ID" + required: true + cancellation_reason: + description: "duplicate, fraudulent, requested_by_customer, abandoned" + stripe_search_payment_intents: + description: 'Search PaymentIntents with Stripe search query (e.g. status:"requires_action" AND amount>5000).' + parameters: + query: + description: "Stripe search query" + required: true + limit: + description: "Number of results" + page: + description: "Cursor for pagination" + stripe_list_refunds: + description: "List refunds processed on the Stripe account. Use for reviewing returned money, refund audits, and tracking refund status." + parameters: + charge: + description: "Filter by charge ID" + payment_intent: + description: "Filter by payment intent ID" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_refund: + description: "Retrieve (get) a single refund by ID." + parameters: + id: + description: "Refund ID (e.g. re_...)" + required: true + stripe_create_refund: + description: "Create a refund (full or partial) for a charge or PaymentIntent. Returns money to the customer." + parameters: + charge: + description: "Charge ID to refund (one of charge or payment_intent required)" + payment_intent: + description: "PaymentIntent ID to refund" + amount: + description: "Amount to refund in smallest currency unit (defaults to full)" + reason: + description: "duplicate, fraudulent, or requested_by_customer" + refund_application_fee: + description: "Boolean — whether to refund the application fee" + reverse_transfer: + description: "Boolean — reverse the transfer to a connected account" + metadata: + description: "Metadata object" + stripe_update_refund: + description: "Update (edit) a refund's metadata." + parameters: + id: + description: "Refund ID" + required: true + metadata: + description: "Metadata object to merge" + stripe_list_disputes: + description: "List chargeback disputes filed against charges on the Stripe account. Use for fraud monitoring, chargeback response workflows, and dispute analytics." + parameters: + charge: + description: "Filter by charge ID" + payment_intent: + description: "Filter by payment intent ID" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_dispute: + description: "Retrieve (get) a single dispute by ID including evidence and status." + parameters: + id: + description: "Dispute ID (e.g. dp_...)" + required: true + stripe_update_dispute: + description: "Update (edit) a dispute to submit evidence for chargeback response." + parameters: + id: + description: "Dispute ID" + required: true + evidence: + description: "Evidence object (e.g. customer_communication, receipt, service_documentation, shipping_documentation, etc.)" + submit: + description: "Boolean — submit evidence immediately" + metadata: + description: "Metadata object" + stripe_list_payouts: + description: "List payouts (transfers from the Stripe balance to a bank account). Use for cash-out tracking, settlement reconciliation, and finance reports." + parameters: + status: + description: "Filter by status (paid, pending, in_transit, canceled, failed)" + destination: + description: "Filter by bank account or card destination ID" + arrival_date: + description: "Filter by arrival_date timestamp" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_payout: + description: "Retrieve (get) a single payout by ID." + parameters: + id: + description: "Payout ID (e.g. po_...)" + required: true + stripe_create_payout: + description: "Create a manual payout from the Stripe balance to the default bank account." + parameters: + amount: + description: "Amount in smallest currency unit" + required: true + currency: + description: "Three-letter ISO currency lowercase" + required: true + description: + description: "Description" + method: + description: "standard or instant" + destination: + description: "Bank account or debit card ID (optional override)" + metadata: + description: "Metadata object" + stripe_list_subscriptions: + description: "List recurring billing subscriptions. Use for MRR/ARR reports, churn analysis, active customer counts, and finding subscriptions in trial, past_due, or canceled state." + parameters: + customer: + description: "Filter by customer ID" + price: + description: "Filter by price ID" + status: + description: "Filter by status (active, past_due, unpaid, canceled, incomplete, incomplete_expired, trialing, all)" + collection_method: + description: "charge_automatically or send_invoice" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_subscription: + description: "Retrieve (get) a single subscription by ID including items, current period, and billing cycle." + parameters: + id: + description: "Subscription ID (e.g. sub_...)" + required: true + stripe_search_subscriptions: + description: 'Search subscriptions using Stripe search query (e.g. status:"trialing" AND created>1700000000).' + parameters: + query: + description: "Stripe search query" + required: true + limit: + description: "Number of results" + page: + description: "Cursor for pagination" + stripe_create_subscription: + description: "Create a new subscription that recurringly bills a customer for one or more prices." + parameters: + customer: + description: "Customer ID to subscribe (required)" + required: true + items: + description: "Array of subscription items, each with a 'price' ID and optional 'quantity'" + required: true + default_payment_method: + description: "Payment method ID to use for invoices" + trial_period_days: + description: "Number of days for free trial" + trial_end: + description: "Unix timestamp when trial ends (or 'now')" + collection_method: + description: "charge_automatically or send_invoice" + days_until_due: + description: "Days until invoice is due (only for send_invoice)" + coupon: + description: "Coupon ID to apply" + metadata: + description: "Metadata object" + stripe_update_subscription: + description: "Update (edit) a subscription's items, prices, quantities, billing settings, or metadata." + parameters: + id: + description: "Subscription ID" + required: true + items: + description: "Updated items array — each item may include 'id' (existing item), 'price', 'quantity', or 'deleted':true" + cancel_at_period_end: + description: "Boolean — cancel at end of current period" + default_payment_method: + description: "Payment method ID" + proration_behavior: + description: "create_prorations, none, or always_invoice" + trial_end: + description: "Unix timestamp or 'now'" + metadata: + description: "Metadata object" + pause_collection: + description: "Object with behavior (keep_as_draft, mark_uncollectible, void) and resumes_at" + stripe_cancel_subscription: + description: "Cancel a subscription immediately (or at period end via stripe_update_subscription with cancel_at_period_end)." + parameters: + id: + description: "Subscription ID" + required: true + invoice_now: + description: "Boolean — generate a final invoice for unbilled usage" + prorate: + description: "Boolean — credit prorated unused time" + cancellation_details: + description: "Object with comment and feedback" + stripe_list_subscription_items: + description: "List items (line items) belonging to a subscription." + parameters: + subscription: + description: "Subscription ID" + required: true + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_subscription_item: + description: "Retrieve (get) a single subscription item by ID." + parameters: + id: + description: "Subscription item ID (e.g. si_...)" + required: true + stripe_list_products: + description: "List products (goods or services sold on Stripe). Use for product catalog browsing and finding SKUs." + parameters: + active: + description: "Filter by active (true/false)" + ids: + description: "Comma-separated product IDs" + shippable: + description: "Filter shippable goods (true/false)" + url: + description: "Filter by URL" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_product: + description: "Retrieve (get) a single product by ID." + parameters: + id: + description: "Product ID (e.g. prod_...)" + required: true + stripe_create_product: + description: "Create a new product to sell." + parameters: + name: + description: "Product name (required)" + required: true + description: + description: "Product description" + active: + description: "Boolean — whether the product can be used" + default_price_data: + description: "Object describing the default price (currency, unit_amount, recurring)" + images: + description: "Array of image URLs" + metadata: + description: "Metadata object" + shippable: + description: "Boolean — whether the product is shippable" + tax_code: + description: "Tax code ID" + url: + description: "Product URL" + stripe_update_product: + description: "Update (edit) a product's name, description, images, or metadata." + parameters: + id: + description: "Product ID" + required: true + name: + description: "New name" + description: + description: "New description" + active: + description: "Boolean" + default_price: + description: "Default price ID" + images: + description: "Array of image URLs" + metadata: + description: "Metadata object" + url: + description: "Product URL" + stripe_delete_product: + description: "Delete (remove) a product. Only succeeds if no prices reference it." + parameters: + id: + description: "Product ID" + required: true + stripe_list_prices: + description: "List prices attached to products. Each price defines how much and how often to charge." + parameters: + product: + description: "Filter by product ID" + active: + description: "Filter by active" + currency: + description: "Filter by currency" + type: + description: "one_time or recurring" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_price: + description: "Retrieve (get) a single price by ID." + parameters: + id: + description: "Price ID (e.g. price_...)" + required: true + stripe_create_price: + description: "Create a new price for a product (one-time or recurring)." + parameters: + product: + description: "Product ID (required if product_data not given)" + product_data: + description: "Inline product object with name to create alongside the price" + currency: + description: "Three-letter ISO currency lowercase (required)" + required: true + unit_amount: + description: "Price in smallest currency unit (required for fixed pricing)" + recurring: + description: "Object with interval (day/week/month/year) and interval_count" + nickname: + description: "Internal display name" + active: + description: "Boolean" + billing_scheme: + description: "per_unit or tiered" + tiers: + description: "Array of tier objects (for tiered pricing)" + tiers_mode: + description: "graduated or volume" + metadata: + description: "Metadata object" + stripe_update_price: + description: "Update (edit) a price's nickname, active flag, or metadata. Note: most fields are immutable on existing prices." + parameters: + id: + description: "Price ID" + required: true + active: + description: "Boolean" + nickname: + description: "Nickname" + metadata: + description: "Metadata object" + stripe_list_invoices: + description: "List invoices on the Stripe account. Use for billing audits, finding past_due/open/paid invoices, and revenue reporting." + parameters: + customer: + description: "Filter by customer ID" + subscription: + description: "Filter by subscription ID" + status: + description: "draft, open, paid, uncollectible, void" + collection_method: + description: "charge_automatically or send_invoice" + created: + description: "Filter by creation timestamp" + due_date: + description: "Filter by due_date timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_invoice: + description: "Retrieve (get) a single invoice by ID with line items, totals, and status." + parameters: + id: + description: "Invoice ID (e.g. in_...)" + required: true + stripe_retrieve_upcoming_invoice: + description: "Retrieve (get) the upcoming (preview) invoice for a customer or subscription before it is finalized. Useful for previewing the next bill." + parameters: + customer: + description: "Customer ID" + subscription: + description: "Subscription ID" + coupon: + description: "Coupon ID for preview" + stripe_search_invoices: + description: 'Search invoices with Stripe search query (e.g. status:"open" AND total>10000).' + parameters: + query: + description: "Stripe search query" + required: true + limit: + description: "Number of results" + page: + description: "Cursor for pagination" + stripe_create_invoice: + description: "Create a draft invoice for a customer. Add invoice items separately or use auto_advance to finalize automatically." + parameters: + customer: + description: "Customer ID (required)" + required: true + auto_advance: + description: "Boolean — finalize and attempt collection automatically" + collection_method: + description: "charge_automatically or send_invoice" + days_until_due: + description: "Days until due (for send_invoice)" + description: + description: "Description" + subscription: + description: "Subscription ID to associate" + metadata: + description: "Metadata object" + stripe_finalize_invoice: + description: "Finalize a draft invoice — locks line items and generates the final amount." + parameters: + id: + description: "Invoice ID" + required: true + auto_advance: + description: "Boolean — proceed with payment collection" + stripe_pay_invoice: + description: "Pay an open invoice using a payment method or by charging the customer's default source." + parameters: + id: + description: "Invoice ID" + required: true + payment_method: + description: "Payment method ID to charge" + paid_out_of_band: + description: "Boolean — mark as paid externally without collecting funds" + off_session: + description: "Boolean" + stripe_send_invoice: + description: "Email an open invoice to the customer." + parameters: + id: + description: "Invoice ID" + required: true + stripe_void_invoice: + description: "Void an open invoice. Cannot be undone." + parameters: + id: + description: "Invoice ID" + required: true + stripe_delete_invoice: + description: "Delete (remove) a draft invoice permanently. Only allowed for draft invoices." + parameters: + id: + description: "Invoice ID" + required: true + stripe_list_invoice_items: + description: "List pending or attached invoice items (line items added to a customer's next invoice)." + parameters: + customer: + description: "Filter by customer ID" + invoice: + description: "Filter by invoice ID" + pending: + description: "Only return pending items (true/false)" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_create_invoice_item: + description: "Create an invoice item that will be added to a customer's next invoice or attached to a specific invoice." + parameters: + customer: + description: "Customer ID (required)" + required: true + amount: + description: "Amount in smallest currency unit" + currency: + description: "Currency code" + description: + description: "Line item description" + price: + description: "Price ID (alternative to amount+currency)" + quantity: + description: "Quantity (defaults to 1)" + invoice: + description: "Optional invoice ID to attach to" + subscription: + description: "Subscription ID to attach to" + metadata: + description: "Metadata object" + stripe_list_payment_methods: + description: "List payment methods attached to a customer (cards, bank accounts, wallets)." + parameters: + customer: + description: "Customer ID (required)" + required: true + type: + description: "card, us_bank_account, sepa_debit, etc." + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_payment_method: + description: "Retrieve (get) a single payment method by ID." + parameters: + id: + description: "Payment method ID (e.g. pm_...)" + required: true + stripe_attach_payment_method: + description: "Attach a payment method to a customer for later reuse." + parameters: + id: + description: "Payment method ID" + required: true + customer: + description: "Customer ID to attach to" + required: true + stripe_detach_payment_method: + description: "Detach (remove) a payment method from its customer." + parameters: + id: + description: "Payment method ID" + required: true + stripe_list_setup_intents: + description: "List SetupIntents (objects representing intent to save a payment method for future use)." + parameters: + customer: + description: "Filter by customer ID" + payment_method: + description: "Filter by payment method ID" + created: + description: "Filter by creation timestamp" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_setup_intent: + description: "Retrieve (get) a single SetupIntent by ID." + parameters: + id: + description: "SetupIntent ID (e.g. seti_...)" + required: true + stripe_create_setup_intent: + description: "Create a SetupIntent to collect a customer's payment method for future use." + parameters: + customer: + description: "Customer ID" + payment_method: + description: "Payment method ID" + payment_method_types: + description: "Array of allowed payment method types" + usage: + description: "on_session or off_session" + confirm: + description: "Boolean — confirm immediately" + description: + description: "Description" + metadata: + description: "Metadata object" + stripe_list_coupons: + description: "List discount coupons defined on the Stripe account." + parameters: + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_coupon: + description: "Retrieve (get) a single coupon by ID." + parameters: + id: + description: "Coupon ID" + required: true + stripe_create_coupon: + description: "Create a discount coupon that can be applied to customers, invoices, or subscriptions." + parameters: + id: + description: "Optional coupon ID (auto-generated if omitted)" + name: + description: "Display name shown on receipts/invoices" + percent_off: + description: "Percentage discount (0-100). Use this or amount_off." + amount_off: + description: "Fixed amount discount in smallest currency unit" + currency: + description: "Required when amount_off is set" + duration: + description: "once, repeating, or forever" + required: true + duration_in_months: + description: "Number of months (for duration=repeating)" + max_redemptions: + description: "Maximum total redemptions" + redeem_by: + description: "Unix timestamp coupon expires for new redemptions" + metadata: + description: "Metadata object" + stripe_delete_coupon: + description: "Delete (remove) a coupon." + parameters: + id: + description: "Coupon ID" + required: true + stripe_list_promotion_codes: + description: "List customer-facing promotion codes (the code strings tied to a coupon)." + parameters: + coupon: + description: "Filter by coupon ID" + customer: + description: "Filter by customer ID" + active: + description: "Filter by active" + code: + description: "Filter by code string" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_create_promotion_code: + description: "Create a customer-facing promotion code tied to a coupon." + parameters: + coupon: + description: "Coupon ID (required)" + required: true + code: + description: "Customer-facing code (auto-generated if omitted)" + customer: + description: "Restrict to a specific customer ID" + max_redemptions: + description: "Maximum total redemptions" + expires_at: + description: "Unix timestamp" + active: + description: "Boolean" + metadata: + description: "Metadata object" + stripe_list_events: + description: "List Stripe events (webhook event log). Use for auditing webhook history, replaying missed events, or finding when a specific object changed." + parameters: + type: + description: "Filter by event type (e.g. charge.succeeded, invoice.paid)" + types: + description: "Array of event types" + created: + description: "Filter by creation timestamp" + delivery_success: + description: "Boolean — filter by webhook delivery outcome" + limit: + description: "Number of objects to return (1-100, default 10)" + starting_after: + description: "Cursor for pagination — an object ID for the next page" + ending_before: + description: "Cursor for pagination — an object ID for the previous page" + stripe_retrieve_event: + description: "Retrieve (get) a single event by ID." + parameters: + id: + description: "Event ID (e.g. evt_...)" + required: true diff --git a/integrations/suno/tools.go b/integrations/suno/tools.go index b43823c1..8304ae3a 100644 --- a/integrations/suno/tools.go +++ b/integrations/suno/tools.go @@ -1,211 +1,12 @@ package suno -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Music Generation ─────────────────────────────────────────── - { - Name: mcp.ToolName("suno_generate_music"), - Description: "Generate a music track with Suno AI. Each request produces 2 songs. Start here for music creation workflows. Stream URL available in ~30s, download URL in ~2-3 min. Use suno_get_generation to poll status", - Parameters: map[string]string{ - "prompt": "Text description or lyrics for the song (500 chars in non-custom mode, up to 5000 in custom mode)", - "style": "Music genre/style (e.g. pop, rock, jazz, folk, synthwave). Required in custom mode", - "title": "Song title (max 80-100 chars depending on model). Required in custom mode", - "model": "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)", - "custom_mode": "Enable custom mode for fine control over style/title/lyrics (true/false, default: true)", - "instrumental": "Generate instrumental only, no vocals (true/false, default: false)", - "callback_url": "Webhook URL for completion notification", - "persona_id": "Persona ID for personalized style", - "negative_tags": "Styles to avoid (e.g. 'Heavy Metal, Upbeat Drums')", - "vocal_gender": "Vocal gender: m or f", - "style_weight": "Style influence weight 0-1 (default: 0.65)", - }, - Required: []string{"prompt"}, - }, - { - Name: mcp.ToolName("suno_get_generation"), - Description: "Check the status of a music generation task. Returns track URLs when complete. Status values: PENDING, TEXT_SUCCESS, FIRST_SUCCESS, SUCCESS, FAILED", - Parameters: map[string]string{"task_id": "Task ID returned from suno_generate_music or other generation tools"}, - Required: []string{"task_id"}, - }, - { - Name: mcp.ToolName("suno_extend_music"), - Description: "Extend an existing audio track with additional content. Creates a continuation from a specific timestamp", - Parameters: map[string]string{ - "audio_id": "ID of the audio track to extend", - "prompt": "Description or lyrics for the extension", - "style": "Music style for the extension", - "title": "Title for the extended track", - "continue_at": "Time in seconds to start extension from", - "model": "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)", - "use_default_params": "Use original track parameters instead of custom (true/false, default: false)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_get_credits"), - Description: "Get the number of remaining generation credits for the authenticated account", - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Lyrics ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("suno_generate_lyrics"), - Description: "Generate song lyrics from a text prompt. Max 200 characters. Use suno_get_lyrics to poll for results", - Parameters: map[string]string{ - "prompt": "Description of desired lyrics (max 200 chars)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"prompt"}, - }, - { - Name: mcp.ToolName("suno_get_lyrics"), - Description: "Get the status and result of a lyrics generation task", - Parameters: map[string]string{"task_id": "Task ID from suno_generate_lyrics"}, - Required: []string{"task_id"}, - }, - { - Name: mcp.ToolName("suno_get_aligned_lyrics"), - Description: "Get timestamped/word-level aligned lyrics for an audio track. Useful for karaoke or synced display", - Parameters: map[string]string{"audio_id": "ID of the audio track"}, - Required: []string{"audio_id"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Audio Processing ─────────────────────────────────────────── - { - Name: mcp.ToolName("suno_separate_stems"), - Description: "Separate an audio track into vocal and instrumental stems. Use suno_get_stem_separation to poll status", - Parameters: map[string]string{ - "audio_id": "ID of the audio track to separate", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_get_stem_separation"), - Description: "Get the status and URLs of a stem separation task. Returns vocal and instrumental track URLs when complete", - Parameters: map[string]string{"task_id": "Task ID from suno_separate_stems"}, - Required: []string{"task_id"}, - }, - { - Name: mcp.ToolName("suno_convert_wav"), - Description: "Convert a generated audio track to WAV format. Use suno_get_wav_conversion to poll status", - Parameters: map[string]string{ - "audio_id": "ID of the audio track to convert", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_get_wav_conversion"), - Description: "Get the status and download URL of a WAV conversion task", - Parameters: map[string]string{"task_id": "Task ID from suno_convert_wav"}, - Required: []string{"task_id"}, - }, - - // ── Advanced Generation ──────────────────────────────────────── - { - Name: mcp.ToolName("suno_cover_audio"), - Description: "Create a cover version of uploaded audio with a new style and arrangement", - Parameters: map[string]string{ - "upload_url": "URL of the source audio file", - "style": "Target music style for the cover", - "title": "Title for the cover version", - "prompt": "Description or lyrics for the cover", - "custom_mode": "Enable custom mode (true/false, default: true)", - "model": "Model version (default: V4_5ALL)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"upload_url"}, - }, - { - Name: mcp.ToolName("suno_upload_extend"), - Description: "Upload audio and extend it with AI-generated continuation", - Parameters: map[string]string{ - "upload_url": "URL of the audio file to extend", - "prompt": "Description or lyrics for the extension", - "style": "Music style for the extension", - "title": "Title for the extended track", - "model": "Model version (default: V4_5ALL)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"upload_url"}, - }, - { - Name: mcp.ToolName("suno_add_vocals"), - Description: "Generate vocal tracks for instrumental music", - Parameters: map[string]string{ - "audio_id": "ID of the instrumental audio track", - "prompt": "Lyrics or vocal description", - "style": "Vocal style", - "model": "Model version (default: V4_5ALL)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_add_instrumental"), - Description: "Generate instrumental accompaniment for a vocal track", - Parameters: map[string]string{ - "audio_id": "ID of the vocal audio track", - "prompt": "Instrumental description", - "style": "Instrumental style", - "model": "Model version (default: V4_5ALL)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_generate_mashup"), - Description: "Generate a mashup combining elements from multiple tracks", - Parameters: map[string]string{ - "audio_ids": "Comma-separated list of audio IDs to mashup", - "style": "Target style for the mashup", - "prompt": "Description of the desired mashup", - "model": "Model version (default: V4_5ALL)", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_ids"}, - }, - - // ── Persona ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("suno_generate_persona"), - Description: "Create a personalized music persona based on generated tracks. Returns a persona_id for use in suno_generate_music", - Parameters: map[string]string{ - "audio_ids": "Comma-separated list of audio IDs to base the persona on", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_ids"}, - }, - - // ── Video ────────────────────────────────────────────────────── - { - Name: mcp.ToolName("suno_generate_video"), - Description: "Generate a music video from an audio track. Use suno_get_video to poll status", - Parameters: map[string]string{ - "audio_id": "ID of the audio track", - "author": "Author/artist name for the video", - "domain_name": "Brand domain name for the video", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, - { - Name: mcp.ToolName("suno_get_video"), - Description: "Get the status and URL of a video generation task", - Parameters: map[string]string{"task_id": "Task ID from suno_generate_video"}, - Required: []string{"task_id"}, - }, - - // ── MIDI ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("suno_generate_midi"), - Description: "Generate a MIDI file from an audio track", - Parameters: map[string]string{ - "audio_id": "ID of the audio track", - "callback_url": "Webhook URL for completion notification", - }, - Required: []string{"audio_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/suno/tools.yaml b/integrations/suno/tools.yaml new file mode 100644 index 00000000..016e63cc --- /dev/null +++ b/integrations/suno/tools.yaml @@ -0,0 +1,214 @@ +version: 1 +tools: + suno_generate_music: + description: "Generate a music track with Suno AI. Each request produces 2 songs. Start here for music creation workflows. Stream URL available in ~30s, download URL in ~2-3 min. Use suno_get_generation to poll status" + parameters: + prompt: + description: "Text description or lyrics for the song (500 chars in non-custom mode, up to 5000 in custom mode)" + required: true + style: + description: "Music genre/style (e.g. pop, rock, jazz, folk, synthwave). Required in custom mode" + title: + description: "Song title (max 80-100 chars depending on model). Required in custom mode" + model: + description: "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)" + custom_mode: + description: "Enable custom mode for fine control over style/title/lyrics (true/false, default: true)" + instrumental: + description: "Generate instrumental only, no vocals (true/false, default: false)" + callback_url: + description: "Webhook URL for completion notification" + persona_id: + description: "Persona ID for personalized style" + negative_tags: + description: "Styles to avoid (e.g. 'Heavy Metal, Upbeat Drums')" + vocal_gender: + description: "Vocal gender: m or f" + style_weight: + description: "Style influence weight 0-1 (default: 0.65)" + suno_get_generation: + description: "Check the status of a music generation task. Returns track URLs when complete. Status values: PENDING, TEXT_SUCCESS, FIRST_SUCCESS, SUCCESS, FAILED" + parameters: + task_id: + description: "Task ID returned from suno_generate_music or other generation tools" + required: true + suno_extend_music: + description: "Extend an existing audio track with additional content. Creates a continuation from a specific timestamp" + parameters: + audio_id: + description: "ID of the audio track to extend" + required: true + prompt: + description: "Description or lyrics for the extension" + style: + description: "Music style for the extension" + title: + description: "Title for the extended track" + continue_at: + description: "Time in seconds to start extension from" + model: + description: "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)" + use_default_params: + description: "Use original track parameters instead of custom (true/false, default: false)" + callback_url: + description: "Webhook URL for completion notification" + suno_get_credits: + description: "Get the number of remaining generation credits for the authenticated account" + suno_generate_lyrics: + description: "Generate song lyrics from a text prompt. Max 200 characters. Use suno_get_lyrics to poll for results" + parameters: + prompt: + description: "Description of desired lyrics (max 200 chars)" + required: true + callback_url: + description: "Webhook URL for completion notification" + suno_get_lyrics: + description: "Get the status and result of a lyrics generation task" + parameters: + task_id: + description: "Task ID from suno_generate_lyrics" + required: true + suno_get_aligned_lyrics: + description: "Get timestamped/word-level aligned lyrics for an audio track. Useful for karaoke or synced display" + parameters: + audio_id: + description: "ID of the audio track" + required: true + suno_separate_stems: + description: "Separate an audio track into vocal and instrumental stems. Use suno_get_stem_separation to poll status" + parameters: + audio_id: + description: "ID of the audio track to separate" + required: true + callback_url: + description: "Webhook URL for completion notification" + suno_get_stem_separation: + description: "Get the status and URLs of a stem separation task. Returns vocal and instrumental track URLs when complete" + parameters: + task_id: + description: "Task ID from suno_separate_stems" + required: true + suno_convert_wav: + description: "Convert a generated audio track to WAV format. Use suno_get_wav_conversion to poll status" + parameters: + audio_id: + description: "ID of the audio track to convert" + required: true + callback_url: + description: "Webhook URL for completion notification" + suno_get_wav_conversion: + description: "Get the status and download URL of a WAV conversion task" + parameters: + task_id: + description: "Task ID from suno_convert_wav" + required: true + suno_cover_audio: + description: "Create a cover version of uploaded audio with a new style and arrangement" + parameters: + upload_url: + description: "URL of the source audio file" + required: true + style: + description: "Target music style for the cover" + title: + description: "Title for the cover version" + prompt: + description: "Description or lyrics for the cover" + custom_mode: + description: "Enable custom mode (true/false, default: true)" + model: + description: "Model version (default: V4_5ALL)" + callback_url: + description: "Webhook URL for completion notification" + suno_upload_extend: + description: "Upload audio and extend it with AI-generated continuation" + parameters: + upload_url: + description: "URL of the audio file to extend" + required: true + prompt: + description: "Description or lyrics for the extension" + style: + description: "Music style for the extension" + title: + description: "Title for the extended track" + model: + description: "Model version (default: V4_5ALL)" + callback_url: + description: "Webhook URL for completion notification" + suno_add_vocals: + description: "Generate vocal tracks for instrumental music" + parameters: + audio_id: + description: "ID of the instrumental audio track" + required: true + prompt: + description: "Lyrics or vocal description" + style: + description: "Vocal style" + model: + description: "Model version (default: V4_5ALL)" + callback_url: + description: "Webhook URL for completion notification" + suno_add_instrumental: + description: "Generate instrumental accompaniment for a vocal track" + parameters: + audio_id: + description: "ID of the vocal audio track" + required: true + prompt: + description: "Instrumental description" + style: + description: "Instrumental style" + model: + description: "Model version (default: V4_5ALL)" + callback_url: + description: "Webhook URL for completion notification" + suno_generate_mashup: + description: "Generate a mashup combining elements from multiple tracks" + parameters: + audio_ids: + description: "Comma-separated list of audio IDs to mashup" + required: true + style: + description: "Target style for the mashup" + prompt: + description: "Description of the desired mashup" + model: + description: "Model version (default: V4_5ALL)" + callback_url: + description: "Webhook URL for completion notification" + suno_generate_persona: + description: "Create a personalized music persona based on generated tracks. Returns a persona_id for use in suno_generate_music" + parameters: + audio_ids: + description: "Comma-separated list of audio IDs to base the persona on" + required: true + callback_url: + description: "Webhook URL for completion notification" + suno_generate_video: + description: "Generate a music video from an audio track. Use suno_get_video to poll status" + parameters: + audio_id: + description: "ID of the audio track" + required: true + author: + description: "Author/artist name for the video" + domain_name: + description: "Brand domain name for the video" + callback_url: + description: "Webhook URL for completion notification" + suno_get_video: + description: "Get the status and URL of a video generation task" + parameters: + task_id: + description: "Task ID from suno_generate_video" + required: true + suno_generate_midi: + description: "Generate a MIDI file from an audio track" + parameters: + audio_id: + description: "ID of the audio track" + required: true + callback_url: + description: "Webhook URL for completion notification" diff --git a/integrations/switchboard/switchboard.go b/integrations/switchboard/switchboard.go index 662949cb..4ee79d95 100644 --- a/integrations/switchboard/switchboard.go +++ b/integrations/switchboard/switchboard.go @@ -4,12 +4,33 @@ import ( "context" _ "embed" "sort" + "sync" + "time" mcp "github.com/daltoniam/switchboard" "github.com/daltoniam/switchboard/compact" "github.com/daltoniam/switchboard/marketplace" ) +// probeBudget caps each Healthy() probe so a single unreachable or +// misconfigured integration cannot stall meta-introspection past this +// bound. Sum-of-slowest latency was the pre-fix failure mode: one stuck +// probe (e.g. ollama on an unreachable host) hung switchboard_list_integrations +// indefinitely because probes ran serially with no per-probe deadline. +// +// Override in tests via withProbeBudget — never mutate from production code. +var probeBudget = 2 * time.Second + +// healthyWithBudget probes a.Healthy with a per-call deadline. Returns +// false if the probe doesn't complete within budget (treated as not +// healthy at the wire boundary). Pure boundary helper — timeout +// responsibility lives here so callers stay flat. +func healthyWithBudget(ctx context.Context, a mcp.Integration, budget time.Duration) bool { + probeCtx, cancel := context.WithTimeout(ctx, budget) + defer cancel() + return a.Healthy(probeCtx) +} + //go:embed compact.yaml var compactYAML []byte @@ -88,6 +109,9 @@ var dispatch = map[mcp.ToolName]handlerFunc{ } // listIntegrations returns all registered integrations with their status. +// +// Health probes for the kept integrations run in parallel with a per-probe +// budget so one unreachable integration cannot stall the meta tool. func listIntegrations(ctx context.Context, s *switchboardInt, args map[string]any) (*mcp.ToolResult, error) { enabledOnly, _ := mcp.ArgBool(args, "enabled_only") @@ -99,28 +123,47 @@ func listIntegrations(ctx context.Context, s *switchboardInt, args map[string]an CredentialKeys []string `json:"credential_keys"` } - var results []integrationSummary - for _, a := range s.services.Registry.All() { + type target struct { + a mcp.Integration + enabled bool + } + + // Pass 1: gather targets (pure — no I/O). + all := s.services.Registry.All() + targets := make([]target, 0, len(all)) + for _, a := range all { ic, exists := s.services.Config.GetIntegration(a.Name()) enabled := exists && ic.Enabled - if enabledOnly && !enabled { continue } + targets = append(targets, target{a: a, enabled: enabled}) + } - var healthy bool - if enabled { - healthy = a.Healthy(ctx) + // Pass 2: parallel probe. Each goroutine writes to its own pre-allocated + // slice cell — no shared mutation, no mutex required. + healthy := make([]bool, len(targets)) + var wg sync.WaitGroup + for i, t := range targets { + if !t.enabled { + continue } + wg.Go(func() { + healthy[i] = healthyWithBudget(ctx, t.a, probeBudget) + }) + } + wg.Wait() - credKeys := s.services.Config.DefaultCredentialKeys(a.Name()) + // Pass 3: assemble (pure). + results := make([]integrationSummary, 0, len(targets)) + for i, t := range targets { + credKeys := s.services.Config.DefaultCredentialKeys(t.a.Name()) sort.Strings(credKeys) - results = append(results, integrationSummary{ - Name: a.Name(), - Enabled: enabled, - Healthy: healthy, - ToolCount: len(a.Tools()), + Name: t.a.Name(), + Enabled: t.enabled, + Healthy: healthy[i], + ToolCount: len(t.a.Tools()), CredentialKeys: credKeys, }) } @@ -152,7 +195,7 @@ func getIntegration(ctx context.Context, s *switchboardInt, args map[string]any) var healthy bool if enabled { - healthy = a.Healthy(ctx) + healthy = healthyWithBudget(ctx, a, probeBudget) } type toolInfo struct { @@ -281,6 +324,9 @@ func configureIntegration(ctx context.Context, s *switchboardInt, args map[strin } // checkHealth checks connectivity for one or all enabled integrations. +// +// The no-name path runs Healthy() probes in parallel under a per-probe budget +// so one unreachable integration cannot stall the meta tool. func checkHealth(ctx context.Context, s *switchboardInt, args map[string]any) (*mcp.ToolResult, error) { name, _ := mcp.ArgStr(args, "name") @@ -304,7 +350,7 @@ func checkHealth(ctx context.Context, s *switchboardInt, args map[string]any) (* var healthy bool if enabled { - healthy = a.Healthy(ctx) + healthy = healthyWithBudget(ctx, a, probeBudget) } return mcp.JSONResult(healthResult{ @@ -314,22 +360,41 @@ func checkHealth(ctx context.Context, s *switchboardInt, args map[string]any) (* }) } - var results []healthResult - for _, a := range s.services.Registry.All() { + type target struct { + a mcp.Integration + enabled bool + } + + // Pass 1: gather targets (pure). + all := s.services.Registry.All() + targets := make([]target, len(all)) + for i, a := range all { ic, exists := s.services.Config.GetIntegration(a.Name()) - enabled := exists && ic.Enabled + targets[i] = target{a: a, enabled: exists && ic.Enabled} + } - var healthy bool - if enabled { - healthy = a.Healthy(ctx) + // Pass 2: parallel probe. Each goroutine owns one cell — no shared mutation. + healthy := make([]bool, len(targets)) + var wg sync.WaitGroup + for i, t := range targets { + if !t.enabled { + continue } - - results = append(results, healthResult{ - Name: a.Name(), - Healthy: healthy, - Enabled: enabled, + wg.Go(func() { + healthy[i] = healthyWithBudget(ctx, t.a, probeBudget) }) } + wg.Wait() + + // Pass 3: assemble (pure). + results := make([]healthResult, len(targets)) + for i, t := range targets { + results[i] = healthResult{ + Name: t.a.Name(), + Healthy: healthy[i], + Enabled: t.enabled, + } + } sort.Slice(results, func(i, j int) bool { return results[i].Name < results[j].Name diff --git a/integrations/switchboard/switchboard_test.go b/integrations/switchboard/switchboard_test.go index ad0ab327..4ff9ad64 100644 --- a/integrations/switchboard/switchboard_test.go +++ b/integrations/switchboard/switchboard_test.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "strings" + "sync/atomic" "testing" + "time" mcp "github.com/daltoniam/switchboard" "github.com/daltoniam/switchboard/compact" @@ -85,6 +87,89 @@ func (f *fakeIntegration) Execute(_ context.Context, toolName mcp.ToolName, _ ma } func (f *fakeIntegration) Healthy(_ context.Context) bool { return f.healthy } +// --- timed integration for concurrency tests --- + +// probeTracker records peak concurrent probes across N timedIntegration +// instances that share it. peak == N proves all probes ran in parallel. +type probeTracker struct { + inFlight atomic.Int32 + peak atomic.Int32 +} + +func (p *probeTracker) enter() { + n := p.inFlight.Add(1) + for { + peak := p.peak.Load() + if n <= peak || p.peak.CompareAndSwap(peak, n) { + break + } + } +} + +func (p *probeTracker) exit() { p.inFlight.Add(-1) } + +// timedIntegration is a fake integration whose Healthy probe sleeps for +// `delay` before returning true. If ctx is cancelled first, returns false +// — used to verify the budget cap. If tracker is non-nil, records peak +// concurrent probes. +type timedIntegration struct { + name string + delay time.Duration + tracker *probeTracker +} + +func (t *timedIntegration) Name() string { return t.name } +func (t *timedIntegration) Configure(_ context.Context, _ mcp.Credentials) error { + return nil +} +func (t *timedIntegration) Tools() []mcp.ToolDefinition { return nil } +func (t *timedIntegration) Execute(_ context.Context, _ mcp.ToolName, _ map[string]any) (*mcp.ToolResult, error) { + return nil, nil +} +func (t *timedIntegration) Healthy(ctx context.Context) bool { + if t.tracker != nil { + t.tracker.enter() + defer t.tracker.exit() + } + // Respect already-cancelled context first — otherwise a delay of 0 + // races the cancellation signal in select (both channels are + // simultaneously ready, scheduler picks randomly). + if err := ctx.Err(); err != nil { + return false + } + timer := time.NewTimer(t.delay) + defer timer.Stop() + select { + case <-timer.C: + return true + case <-ctx.Done(): + return false + } +} + +// withProbeBudget overrides the package-level probeBudget for the duration +// of the test and restores it on cleanup. +func withProbeBudget(t *testing.T, b time.Duration) { + t.Helper() + orig := probeBudget + probeBudget = b + t.Cleanup(func() { probeBudget = orig }) +} + +func newTimedTestServices(timed ...*timedIntegration) *mcp.Services { + reg := registry.New() + integrations := map[string]*mcp.IntegrationConfig{} + for _, ti := range timed { + _ = reg.Register(ti) + integrations[ti.name] = &mcp.IntegrationConfig{Enabled: true} + } + return &mcp.Services{ + Config: newMockConfigService(integrations), + Registry: reg, + Metrics: mcp.NewMetrics(), + } +} + // --- test helpers --- func newTestServices(fakeIntegrations ...*fakeIntegration) *mcp.Services { @@ -396,6 +481,140 @@ func TestCheckHealth_All(t *testing.T) { assert.Len(t, results, 2) } +// --- Per-probe budget + parallel fan-out tests --- + +// TestHealthyWithBudget verifies the helper enforces a per-probe timeout. +// A misconfigured or unreachable integration must not exceed the budget; +// the helper returns false in that case. +func TestHealthyWithBudget(t *testing.T) { + tests := []struct { + name string + delay time.Duration + budget time.Duration + want bool + }{ + {"probe completes within budget", 5 * time.Millisecond, 100 * time.Millisecond, true}, + {"probe exceeds budget returns false", 200 * time.Millisecond, 50 * time.Millisecond, false}, + {"already-cancelled context returns false", 0, 100 * time.Millisecond, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ti := &timedIntegration{name: "t", delay: tt.delay} + ctx := context.Background() + if strings.HasPrefix(tt.name, "already-cancelled") { + cancelled, cancel := context.WithCancel(ctx) + cancel() + ctx = cancelled + } + start := time.Now() + got := healthyWithBudget(ctx, ti, tt.budget) + elapsed := time.Since(start) + assert.Equal(t, tt.want, got, "Healthy result") + assert.LessOrEqual(t, elapsed, tt.budget+30*time.Millisecond, + "probe must not exceed budget by more than scheduler slack") + }) + } +} + +// TestListIntegrations_RunsHealthProbesInParallel proves that probes for +// multiple integrations run concurrently — peak inFlight equals the +// integration count. Without parallel fan-out, peak would be 1 (serial). +func TestListIntegrations_RunsHealthProbesInParallel(t *testing.T) { + tracker := &probeTracker{} + const n = 4 + const probeDelay = 60 * time.Millisecond + + ints := make([]*timedIntegration, n) + for i := range ints { + ints[i] = &timedIntegration{ + name: string(rune('a' + i)), + delay: probeDelay, + tracker: tracker, + } + } + services := newTimedTestServices(ints...) + s := newTestIntegration(services) + + start := time.Now() + res, err := listIntegrations(context.Background(), s, map[string]any{"enabled_only": true}) + elapsed := time.Since(start) + require.NoError(t, err) + assert.False(t, res.IsError) + + assert.Equal(t, int32(n), tracker.peak.Load(), + "all %d probes must run concurrently", n) + assert.Less(t, elapsed, probeDelay*time.Duration(n-1), + "parallel total must be strictly less than serial total (n*delay)") +} + +// TestListIntegrations_SlowIntegrationDoesNotBlockOthers combines parallelism +// with the per-probe budget: one slow integration times out at the budget +// while fast integrations report their real result, all within ~budget. +func TestListIntegrations_SlowIntegrationDoesNotBlockOthers(t *testing.T) { + withProbeBudget(t, 80*time.Millisecond) + tracker := &probeTracker{} + + fast1 := &timedIntegration{name: "fast1", delay: 5 * time.Millisecond, tracker: tracker} + fast2 := &timedIntegration{name: "fast2", delay: 5 * time.Millisecond, tracker: tracker} + slow := &timedIntegration{name: "slow", delay: 500 * time.Millisecond, tracker: tracker} + + services := newTimedTestServices(fast1, fast2, slow) + s := newTestIntegration(services) + + start := time.Now() + res, err := listIntegrations(context.Background(), s, map[string]any{"enabled_only": true}) + elapsed := time.Since(start) + require.NoError(t, err) + require.False(t, res.IsError) + + // Total wall time bounded by budget (not by the slow integration's 500ms). + assert.Less(t, elapsed, 200*time.Millisecond, + "total must be ~budget (80ms) + slack, not the slow integration's 500ms") + + var results []map[string]any + require.NoError(t, json.Unmarshal([]byte(res.Data), &results)) + byName := map[string]map[string]any{} + for _, r := range results { + byName[r["name"].(string)] = r + } + // Fast integrations reported their real probe result (true). + assert.Equal(t, true, byName["fast1"]["healthy"], "fast1 must report its real result") + assert.Equal(t, true, byName["fast2"]["healthy"], "fast2 must report its real result") + // Slow integration timed out — wire shape omits healthy:false via omitempty. + _, slowHasHealthy := byName["slow"]["healthy"] + assert.False(t, slowHasHealthy, "slow integration must not report healthy:true after budget exceeded") +} + +// TestCheckHealth_AllRunsHealthProbesInParallel mirrors the listIntegrations +// concurrency test for the checkHealth no-name path. +func TestCheckHealth_AllRunsHealthProbesInParallel(t *testing.T) { + tracker := &probeTracker{} + const n = 3 + const probeDelay = 60 * time.Millisecond + + ints := make([]*timedIntegration, n) + for i := range ints { + ints[i] = &timedIntegration{ + name: string(rune('a' + i)), + delay: probeDelay, + tracker: tracker, + } + } + services := newTimedTestServices(ints...) + s := newTestIntegration(services) + + start := time.Now() + res, err := checkHealth(context.Background(), s, map[string]any{}) + elapsed := time.Since(start) + require.NoError(t, err) + require.False(t, res.IsError) + + assert.Equal(t, int32(n), tracker.peak.Load(), + "checkHealth must run all %d probes concurrently", n) + assert.Less(t, elapsed, probeDelay*time.Duration(n-1), + "parallel total must be strictly less than serial total") +} + func TestBrowsePlugins_NilMarketplace(t *testing.T) { services := newTestServices() s := newTestIntegration(services) diff --git a/integrations/switchboard/tools.go b/integrations/switchboard/tools.go index 541d7155..d866b22f 100644 --- a/integrations/switchboard/tools.go +++ b/integrations/switchboard/tools.go @@ -1,79 +1,12 @@ package switchboard -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - { - Name: "switchboard_list_integrations", - Description: "List all registered integrations with their enabled/healthy status, tool counts, and credential keys. " + - "Start here for Switchboard self-management, configuration, and integration setup. " + - "Shows which integrations are configured, which need credentials, and which are healthy.", - Parameters: map[string]string{ - "enabled_only": "If true, only show enabled integrations (default: false).", - }, - }, - { - Name: "switchboard_get_integration", - Description: "Get detailed information about a specific integration including credential keys (not values), " + - "tool list, enabled/healthy status, and configuration hints. " + - "Use after list_integrations to inspect a single integration before configuring it.", - Parameters: map[string]string{ - "name": "Integration name (e.g. \"github\", \"datadog\", \"slack\").", - }, - Required: []string{"name"}, - }, - { - Name: "switchboard_configure_integration", - Description: "Configure an integration by setting credentials and enabling or disabling it. " + - "Credentials are merged with existing values — send only the keys you want to update. " + - "Set enabled=false to disable an integration without removing its credentials.", - Parameters: map[string]string{ - "name": "Integration name (e.g. \"github\", \"datadog\").", - "credentials": "JSON object of credential key-value pairs to set (merged with existing).", - "enabled": "Whether to enable the integration after configuring (default: true).", - }, - Required: []string{"name"}, - }, - { - Name: "switchboard_check_health", - Description: "Check connectivity health of one or all enabled integrations. " + - "Returns healthy/unhealthy status for each integration by calling its health endpoint. " + - "Use to verify credentials work and upstream APIs are reachable.", - Parameters: map[string]string{ - "name": "Optional: specific integration to check. Omit to check all enabled integrations.", - }, - }, - { - Name: "switchboard_browse_plugins", - Description: "Browse available plugins from configured marketplace manifest sources. " + - "Shows plugin name, description, latest version, and whether it is already installed. " + - "Use before install_plugin to discover available extensions.", - Parameters: map[string]string{}, - }, - { - Name: "switchboard_install_plugin", - Description: "Install a plugin from the marketplace by name or from a direct URL. " + - "Downloads the WASM module, verifies its checksum, and registers it. " + - "Requires a server restart to load the plugin. Use after browse_plugins.", - Parameters: map[string]string{ - "name": "Plugin name from the marketplace (mutually exclusive with url).", - "url": "Direct URL to a .wasm file to install (mutually exclusive with name).", - }, - }, - { - Name: "switchboard_uninstall_plugin", - Description: "Uninstall a marketplace plugin by name. Removes the WASM file and " + - "deregisters it from config. Requires a server restart to take effect.", - Parameters: map[string]string{ - "name": "Name of the installed plugin to remove.", - }, - Required: []string{"name"}, - }, - { - Name: "switchboard_server_info", - Description: "Get Switchboard server information including version, enabled integration count, " + - "total tool count, marketplace status, and runtime metrics summary. " + - "Use for diagnostics, status checks, and understanding the current server state.", - Parameters: map[string]string{}, - }, -} + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/switchboard/tools.yaml b/integrations/switchboard/tools.yaml new file mode 100644 index 00000000..2a4da0cd --- /dev/null +++ b/integrations/switchboard/tools.yaml @@ -0,0 +1,47 @@ +version: 1 +tools: + switchboard_list_integrations: + description: "List all registered integrations with their enabled/healthy status, tool counts, and credential keys. Start here for Switchboard self-management, configuration, and integration setup. Shows which integrations are configured, which need credentials, and which are healthy." + parameters: + enabled_only: + description: "If true, only show enabled integrations (default: false)." + switchboard_get_integration: + description: "Get detailed information about a specific integration including credential keys (not values), tool list, enabled/healthy status, and configuration hints. Use after list_integrations to inspect a single integration before configuring it." + parameters: + name: + description: 'Integration name (e.g. "github", "datadog", "slack").' + required: true + switchboard_configure_integration: + description: "Configure an integration by setting credentials and enabling or disabling it. Credentials are merged with existing values — send only the keys you want to update. Set enabled=false to disable an integration without removing its credentials." + parameters: + name: + description: 'Integration name (e.g. "github", "datadog").' + required: true + credentials: + description: "JSON object of credential key-value pairs to set (merged with existing)." + enabled: + description: "Whether to enable the integration after configuring (default: true)." + switchboard_check_health: + description: "Check connectivity health of one or all enabled integrations. Returns healthy/unhealthy status for each integration by calling its health endpoint. Use to verify credentials work and upstream APIs are reachable." + parameters: + name: + description: "Optional: specific integration to check. Omit to check all enabled integrations." + switchboard_browse_plugins: + description: "Browse available plugins from configured marketplace manifest sources. Shows plugin name, description, latest version, and whether it is already installed. Use before install_plugin to discover available extensions." + parameters: {} + switchboard_install_plugin: + description: "Install a plugin from the marketplace by name or from a direct URL. Downloads the WASM module, verifies its checksum, and registers it. Requires a server restart to load the plugin. Use after browse_plugins." + parameters: + name: + description: "Plugin name from the marketplace (mutually exclusive with url)." + url: + description: "Direct URL to a .wasm file to install (mutually exclusive with name)." + switchboard_uninstall_plugin: + description: "Uninstall a marketplace plugin by name. Removes the WASM file and deregisters it from config. Requires a server restart to take effect." + parameters: + name: + description: "Name of the installed plugin to remove." + required: true + switchboard_server_info: + description: "Get Switchboard server information including version, enabled integration count, total tool count, marketplace status, and runtime metrics summary. Use for diagnostics, status checks, and understanding the current server state." + parameters: {} diff --git a/integrations/vercel/tools.go b/integrations/vercel/tools.go index ce8a0202..bd758060 100644 --- a/integrations/vercel/tools.go +++ b/integrations/vercel/tools.go @@ -1,305 +1,12 @@ package vercel -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - { - Name: mcp.ToolName("vercel_list_teams"), - Description: "List Vercel teams and workspaces available to the token. Start here to discover team_id or team_slug for scoped project, deployment, domain, and environment workflows.", - Parameters: map[string]string{ - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "since": "Lower timestamp cursor/filter in milliseconds", - "until": "Upper timestamp cursor/filter in milliseconds", - }, - }, - { - Name: mcp.ToolName("vercel_get_team"), - Description: "Get details for a Vercel team or workspace. Use after vercel_list_teams.", - Parameters: map[string]string{"team_id": "Team identifier (team_...)", "team_slug": "Team URL slug if team_id is not known"}, - }, - { - Name: mcp.ToolName("vercel_list_team_members"), - Description: "List Vercel team members and roles. Use after vercel_list_teams to inspect workspace access.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "search": "Search by name, username, or email", - "role": "Filter by team role", - }, - }, - { - Name: mcp.ToolName("vercel_list_user_events"), - Description: "List Vercel audit and activity events such as deployments, login activity, and team changes.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "since": "Lower timestamp cursor/filter in milliseconds", - "until": "Upper timestamp cursor/filter in milliseconds", - "types": "Comma-separated event types", - "project_ids": "Comma-separated project IDs", - "principal_id": "User or team principal ID", - "with_payload": "Include event payload details (true/false)", - }, - }, - { - Name: mcp.ToolName("vercel_list_projects"), - Description: "List Vercel projects, apps, frontend sites, and repositories. Start here for deployment, environment variable, domain, and alias workflows.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "search": "Search projects by name", - }, - }, - { - Name: mcp.ToolName("vercel_get_project"), - Description: "Get Vercel project details including framework, repository linkage, targets, and configuration. Use after vercel_list_projects.", - Parameters: map[string]string{ - "id_or_name": "Project ID or name", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"id_or_name"}, - }, - { - Name: mcp.ToolName("vercel_create_project"), - Description: "Create a Vercel project. Provide body with project settings such as name, framework, gitRepository, buildCommand, outputDirectory, and installCommand.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "body": "Project create request object", - }, - Required: []string{"body"}, - }, - { - Name: mcp.ToolName("vercel_update_project"), - Description: "Update Vercel project settings. Use after vercel_get_project to modify build, framework, command, or environment configuration.", - Parameters: map[string]string{ - "id_or_name": "Project ID or name", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "body": "Project update request object", - }, - Required: []string{"id_or_name", "body"}, - }, - { - Name: mcp.ToolName("vercel_delete_project"), - Description: "Delete a Vercel project. Use after vercel_list_projects or vercel_get_project when removing an app/site.", - Parameters: map[string]string{ - "id_or_name": "Project ID or name", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"id_or_name"}, - }, - { - Name: mcp.ToolName("vercel_list_deployments"), - Description: "List Vercel deployments, deploy history, build status, preview URLs, production rollouts, and failed deploys. Start here for CI/CD deployment debugging.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "since": "Lower timestamp cursor/filter in milliseconds", - "until": "Upper timestamp cursor/filter in milliseconds", - "project_id": "Project ID filter", - "app": "Project/app name filter", - "state": "Deployment state: BUILDING, ERROR, READY, CANCELED, QUEUED, INITIALIZING", - "target": "Deployment target: production, preview, or staging", - }, - }, - { - Name: mcp.ToolName("vercel_get_deployment"), - Description: "Get details for a specific Vercel deployment including state, URL, git source, target, and build metadata. Use after vercel_list_deployments.", - Parameters: map[string]string{ - "deployment_id": "Deployment ID (dpl_...)", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("vercel_create_deployment"), - Description: "Create or redeploy a Vercel deployment. Provide body with name, files, gitMetadata, projectSettings, target, deploymentId, or withLatestCommit.", - Parameters: map[string]string{ - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "body": "Deployment create request object", - "force_new": "Force new deployment instead of reusing build cache (true/false)", - }, - Required: []string{"body"}, - }, - { - Name: mcp.ToolName("vercel_cancel_deployment"), - Description: "Cancel a queued or building Vercel deployment. Use after vercel_list_deployments when a build is stuck or no longer needed.", - Parameters: map[string]string{ - "deployment_id": "Deployment ID (dpl_...)", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("vercel_delete_deployment"), - Description: "Delete a Vercel deployment. Use after vercel_list_deployments to remove old preview or failed deployments.", - Parameters: map[string]string{ - "deployment_id": "Deployment ID (dpl_...)", - "url": "Deployment URL alternative identifier", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("vercel_list_deployment_events"), - Description: "List Vercel deployment build events and logs. Use after vercel_list_deployments or vercel_get_deployment to debug build errors and failed deploys.", - Parameters: map[string]string{ - "deployment_id_or_url": "Deployment ID or URL", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 100)", - "since": "Lower timestamp cursor/filter in milliseconds", - "until": "Upper timestamp cursor/filter in milliseconds", - "direction": "Log direction: forward or backward", - "name": "Build step name filter", - "status_code": "HTTP status code filter", - "builds": "Include build logs/events (true/false)", - }, - Required: []string{"deployment_id_or_url"}, - }, - { - Name: mcp.ToolName("vercel_list_runtime_logs"), - Description: "List Vercel runtime logs for a deployment. Use after vercel_get_deployment to inspect function, edge, and request logs.", - Parameters: map[string]string{ - "project_id": "Project ID", - "deployment_id": "Deployment ID", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 100)", - "since": "Lower timestamp cursor/filter in milliseconds", - "until": "Upper timestamp cursor/filter in milliseconds", - "next": "Pagination cursor from previous response", - }, - Required: []string{"project_id", "deployment_id"}, - }, - { - Name: mcp.ToolName("vercel_list_project_env_vars"), - Description: "List Vercel project environment variables and secret metadata. Use after vercel_get_project to inspect production, preview, and development config.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "limit": "Results per page (default 20)", - "next": "Pagination cursor from previous response", - "git_branch": "Filter environment variables by git branch", - "target": "Filter by target: production, preview, or development", - }, - Required: []string{"project_id_or_name"}, - }, - { - Name: mcp.ToolName("vercel_create_project_env_vars"), - Description: "Create one or more Vercel project environment variables. Values are write-only; use after vercel_get_project.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "envs": "Array of env var objects with key, value, target, type, gitBranch, comment", - "upsert": "Update existing variables with same key/target (true/false)", - }, - Required: []string{"project_id_or_name", "envs"}, - }, - { - Name: mcp.ToolName("vercel_update_project_env_var"), - Description: "Update a Vercel project environment variable by ID. Use after vercel_list_project_env_vars.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "env_id": "Environment variable ID", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - "body": "Environment variable update object", - }, - Required: []string{"project_id_or_name", "env_id", "body"}, - }, - { - Name: mcp.ToolName("vercel_delete_project_env_var"), - Description: "Delete a Vercel project environment variable by ID. Use after vercel_list_project_env_vars.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "env_id": "Environment variable ID", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"project_id_or_name", "env_id"}, - }, - { - Name: mcp.ToolName("vercel_add_project_domain"), - Description: "Add a domain to a Vercel project. Use after vercel_get_project when configuring custom domains.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "domain": "Domain name to add", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"project_id_or_name", "domain"}, - }, - { - Name: mcp.ToolName("vercel_remove_project_domain"), - Description: "Remove a domain from a Vercel project. Use after vercel_get_project or vercel_get_domain_config.", - Parameters: map[string]string{ - "project_id_or_name": "Project ID or name", - "domain": "Domain name to remove", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"project_id_or_name", "domain"}, - }, - { - Name: mcp.ToolName("vercel_get_domain_config"), - Description: "Get Vercel domain DNS configuration and misconfiguration status. Use when debugging custom domain setup.", - Parameters: map[string]string{ - "domain": "Domain name", - "project_id_or_name": "Optional project ID or name for stricter checks", - "strict": "Perform strict configuration check (true/false)", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"domain"}, - }, - { - Name: mcp.ToolName("vercel_list_deployment_aliases"), - Description: "List aliases assigned to a Vercel deployment. Use after vercel_get_deployment to inspect production and preview URLs.", - Parameters: map[string]string{ - "deployment_id": "Deployment ID (dpl_...)", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"deployment_id"}, - }, - { - Name: mcp.ToolName("vercel_assign_deployment_alias"), - Description: "Assign a custom alias URL to a Vercel deployment. Use after vercel_get_deployment for manual alias promotion.", - Parameters: map[string]string{ - "deployment_id": "Deployment ID (dpl_...)", - "alias": "Alias domain or URL to assign", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"deployment_id", "alias"}, - }, - { - Name: mcp.ToolName("vercel_delete_deployment_alias"), - Description: "Delete a Vercel deployment alias by alias ID. Use after vercel_list_deployment_aliases.", - Parameters: map[string]string{ - "alias_id": "Alias ID", - "team_id": "Team identifier (defaults to configured team_id)", - "team_slug": "Team slug (defaults to configured team_slug when team_id is empty)", - }, - Required: []string{"alias_id"}, - }, -} + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/vercel/tools.yaml b/integrations/vercel/tools.yaml new file mode 100644 index 00000000..2c8a5288 --- /dev/null +++ b/integrations/vercel/tools.yaml @@ -0,0 +1,361 @@ +version: 1 +tools: + vercel_list_teams: + description: "List Vercel teams and workspaces available to the token. Start here to discover team_id or team_slug for scoped project, deployment, domain, and environment workflows." + parameters: + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + since: + description: "Lower timestamp cursor/filter in milliseconds" + until: + description: "Upper timestamp cursor/filter in milliseconds" + vercel_get_team: + description: "Get details for a Vercel team or workspace. Use after vercel_list_teams." + parameters: + team_id: + description: "Team identifier (team_...)" + team_slug: + description: "Team URL slug if team_id is not known" + vercel_list_team_members: + description: "List Vercel team members and roles. Use after vercel_list_teams to inspect workspace access." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + search: + description: "Search by name, username, or email" + role: + description: "Filter by team role" + vercel_list_user_events: + description: "List Vercel audit and activity events such as deployments, login activity, and team changes." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + since: + description: "Lower timestamp cursor/filter in milliseconds" + until: + description: "Upper timestamp cursor/filter in milliseconds" + types: + description: "Comma-separated event types" + project_ids: + description: "Comma-separated project IDs" + principal_id: + description: "User or team principal ID" + with_payload: + description: "Include event payload details (true/false)" + vercel_list_projects: + description: "List Vercel projects, apps, frontend sites, and repositories. Start here for deployment, environment variable, domain, and alias workflows." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + search: + description: "Search projects by name" + vercel_get_project: + description: "Get Vercel project details including framework, repository linkage, targets, and configuration. Use after vercel_list_projects." + parameters: + id_or_name: + description: "Project ID or name" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_create_project: + description: "Create a Vercel project. Provide body with project settings such as name, framework, gitRepository, buildCommand, outputDirectory, and installCommand." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + body: + description: "Project create request object" + required: true + vercel_update_project: + description: "Update Vercel project settings. Use after vercel_get_project to modify build, framework, command, or environment configuration." + parameters: + id_or_name: + description: "Project ID or name" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + body: + description: "Project update request object" + required: true + vercel_delete_project: + description: "Delete a Vercel project. Use after vercel_list_projects or vercel_get_project when removing an app/site." + parameters: + id_or_name: + description: "Project ID or name" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_list_deployments: + description: "List Vercel deployments, deploy history, build status, preview URLs, production rollouts, and failed deploys. Start here for CI/CD deployment debugging." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + since: + description: "Lower timestamp cursor/filter in milliseconds" + until: + description: "Upper timestamp cursor/filter in milliseconds" + project_id: + description: "Project ID filter" + app: + description: "Project/app name filter" + state: + description: "Deployment state: BUILDING, ERROR, READY, CANCELED, QUEUED, INITIALIZING" + target: + description: "Deployment target: production, preview, or staging" + vercel_get_deployment: + description: "Get details for a specific Vercel deployment including state, URL, git source, target, and build metadata. Use after vercel_list_deployments." + parameters: + deployment_id: + description: "Deployment ID (dpl_...)" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_create_deployment: + description: "Create or redeploy a Vercel deployment. Provide body with name, files, gitMetadata, projectSettings, target, deploymentId, or withLatestCommit." + parameters: + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + body: + description: "Deployment create request object" + required: true + force_new: + description: "Force new deployment instead of reusing build cache (true/false)" + vercel_cancel_deployment: + description: "Cancel a queued or building Vercel deployment. Use after vercel_list_deployments when a build is stuck or no longer needed." + parameters: + deployment_id: + description: "Deployment ID (dpl_...)" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_delete_deployment: + description: "Delete a Vercel deployment. Use after vercel_list_deployments to remove old preview or failed deployments." + parameters: + deployment_id: + description: "Deployment ID (dpl_...)" + required: true + url: + description: "Deployment URL alternative identifier" + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_list_deployment_events: + description: "List Vercel deployment build events and logs. Use after vercel_list_deployments or vercel_get_deployment to debug build errors and failed deploys." + parameters: + deployment_id_or_url: + description: "Deployment ID or URL" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 100)" + since: + description: "Lower timestamp cursor/filter in milliseconds" + until: + description: "Upper timestamp cursor/filter in milliseconds" + direction: + description: "Log direction: forward or backward" + name: + description: "Build step name filter" + status_code: + description: "HTTP status code filter" + builds: + description: "Include build logs/events (true/false)" + vercel_list_runtime_logs: + description: "List Vercel runtime logs for a deployment. Use after vercel_get_deployment to inspect function, edge, and request logs." + parameters: + project_id: + description: "Project ID" + required: true + deployment_id: + description: "Deployment ID" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 100)" + since: + description: "Lower timestamp cursor/filter in milliseconds" + until: + description: "Upper timestamp cursor/filter in milliseconds" + next: + description: "Pagination cursor from previous response" + vercel_list_project_env_vars: + description: "List Vercel project environment variables and secret metadata. Use after vercel_get_project to inspect production, preview, and development config." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + limit: + description: "Results per page (default 20)" + next: + description: "Pagination cursor from previous response" + git_branch: + description: "Filter environment variables by git branch" + target: + description: "Filter by target: production, preview, or development" + vercel_create_project_env_vars: + description: "Create one or more Vercel project environment variables. Values are write-only; use after vercel_get_project." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + envs: + description: "Array of env var objects with key, value, target, type, gitBranch, comment" + required: true + upsert: + description: "Update existing variables with same key/target (true/false)" + vercel_update_project_env_var: + description: "Update a Vercel project environment variable by ID. Use after vercel_list_project_env_vars." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + env_id: + description: "Environment variable ID" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + body: + description: "Environment variable update object" + required: true + vercel_delete_project_env_var: + description: "Delete a Vercel project environment variable by ID. Use after vercel_list_project_env_vars." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + env_id: + description: "Environment variable ID" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_add_project_domain: + description: "Add a domain to a Vercel project. Use after vercel_get_project when configuring custom domains." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + domain: + description: "Domain name to add" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_remove_project_domain: + description: "Remove a domain from a Vercel project. Use after vercel_get_project or vercel_get_domain_config." + parameters: + project_id_or_name: + description: "Project ID or name" + required: true + domain: + description: "Domain name to remove" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_get_domain_config: + description: "Get Vercel domain DNS configuration and misconfiguration status. Use when debugging custom domain setup." + parameters: + domain: + description: "Domain name" + required: true + project_id_or_name: + description: "Optional project ID or name for stricter checks" + strict: + description: "Perform strict configuration check (true/false)" + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_list_deployment_aliases: + description: "List aliases assigned to a Vercel deployment. Use after vercel_get_deployment to inspect production and preview URLs." + parameters: + deployment_id: + description: "Deployment ID (dpl_...)" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_assign_deployment_alias: + description: "Assign a custom alias URL to a Vercel deployment. Use after vercel_get_deployment for manual alias promotion." + parameters: + deployment_id: + description: "Deployment ID (dpl_...)" + required: true + alias: + description: "Alias domain or URL to assign" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" + vercel_delete_deployment_alias: + description: "Delete a Vercel deployment alias by alias ID. Use after vercel_list_deployment_aliases." + parameters: + alias_id: + description: "Alias ID" + required: true + team_id: + description: "Team identifier (defaults to configured team_id)" + team_slug: + description: "Team slug (defaults to configured team_slug when team_id is empty)" diff --git a/integrations/webfetch/tools.go b/integrations/webfetch/tools.go index 56c73b10..cc9af569 100644 --- a/integrations/webfetch/tools.go +++ b/integrations/webfetch/tools.go @@ -1,19 +1,12 @@ package webfetch -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - { - Name: "web_fetch", - Description: "Fetches the content of a URL and returns it as readable text. " + - "Use this to read documentation, API references, README files, changelogs, " + - "GitHub raw content, package docs, or any web page whose content you need to " + - "reason about. Returns extracted readable text, not raw HTML. " + - "Start here for web browsing, URL reading, and online documentation lookup.", - Parameters: map[string]string{ - "url": "The full URL to fetch (https only).", - "timeout": "Request timeout in seconds (default 10, max 30).", - }, - Required: []string{"url"}, - }, -} + mcp "github.com/daltoniam/switchboard" +) + +//go:embed tools.yaml +var toolsYAML []byte + +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/webfetch/tools.yaml b/integrations/webfetch/tools.yaml new file mode 100644 index 00000000..07fe6a10 --- /dev/null +++ b/integrations/webfetch/tools.yaml @@ -0,0 +1,10 @@ +version: 1 +tools: + web_fetch: + description: "Fetches the content of a URL and returns it as readable text. Use this to read documentation, API references, README files, changelogs, GitHub raw content, package docs, or any web page whose content you need to reason about. Returns extracted readable text, not raw HTML. Start here for web browsing, URL reading, and online documentation lookup." + parameters: + url: + description: "The full URL to fetch (https only)." + required: true + timeout: + description: "Request timeout in seconds (default 10, max 30)." diff --git a/integrations/x/tools.go b/integrations/x/tools.go index 427925ce..465d283f 100644 --- a/integrations/x/tools.go +++ b/integrations/x/tools.go @@ -1,498 +1,12 @@ package x -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── Tweets ───────────────────────────────────────────────────── - { - Name: "x_get_tweet", Description: "Get a single tweet by ID. Returns full tweet object with text, author, metrics, and entities", - Parameters: map[string]string{ - "id": "Tweet ID", - "tweet_fields": "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text,conversation_id,reply_settings", - "expansions": "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys", - "user_fields": "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics", - }, - Required: []string{"id"}, - }, - { - Name: "x_get_tweets", Description: "Get multiple tweets by IDs (up to 100). Start here for bulk tweet lookups", - Parameters: map[string]string{ - "ids": "Comma-separated tweet IDs (max 100)", - "tweet_fields": "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text", - "expansions": "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys", - "user_fields": "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics", - }, - Required: []string{"ids"}, - }, - { - Name: "x_create_tweet", Description: "Post a new tweet. Supports text, replies, quotes, and polls", - Parameters: map[string]string{ - "text": "Tweet text (up to 280 chars)", - "reply_to": "Tweet ID to reply to", - "quote_tweet_id": "Tweet ID to quote", - "poll_options": "Comma-separated poll options (2-4 choices)", - "poll_duration_minutes": "Poll duration in minutes (5-10080)", - }, - Required: []string{"text"}, - }, - { - Name: "x_delete_tweet", Description: "Delete a tweet by ID. Must be authored by the authenticated user", - Parameters: map[string]string{"id": "Tweet ID to delete"}, - Required: []string{"id"}, - }, - { - Name: "x_search_recent", Description: "Search tweets from the last 7 days. Start here for most tweet discovery workflows. Supports the full X search query syntax", - Parameters: map[string]string{ - "query": "Search query (required). Supports operators: from:, to:, is:retweet, has:media, has:links, lang:, -is:retweet, etc.", - "max_results": "Results per page (10-100, default 10)", - "next_token": "Pagination token from previous response", - "tweet_fields": "Comma-separated tweet fields: author_id,created_at,public_metrics,source,text,conversation_id,entities", - "start_time": "Oldest UTC datetime (ISO 8601: 2024-01-01T00:00:00Z)", - "end_time": "Newest UTC datetime (ISO 8601)", - "sort_order": "Order of results: recency or relevancy", - }, - Required: []string{"query"}, - }, - { - Name: "x_search_all", Description: "Full-archive search across all tweets (requires Pro tier or higher). Use x_search_recent for 7-day window instead", - Parameters: map[string]string{ - "query": "Search query (required). Same operators as x_search_recent", - "max_results": "Results per page (10-500, default 10)", - "next_token": "Pagination token from previous response", - "tweet_fields": "Comma-separated tweet fields", - "start_time": "Oldest UTC datetime (ISO 8601)", - "end_time": "Newest UTC datetime (ISO 8601)", - "sort_order": "Order of results: recency or relevancy", - }, - Required: []string{"query"}, - }, - { - Name: "x_get_tweet_count", Description: "Get count of tweets matching a search query from the last 7 days. Useful for analytics without retrieving full tweets", - Parameters: map[string]string{ - "query": "Search query", - "granularity": "Aggregation granularity: minute, hour, or day (default day)", - "start_time": "Oldest UTC datetime (ISO 8601)", - "end_time": "Newest UTC datetime (ISO 8601)", - }, - Required: []string{"query"}, - }, - { - Name: "x_get_quote_tweets", Description: "Get tweets that quote a specific tweet", - Parameters: map[string]string{ - "id": "Tweet ID to get quotes for", - "max_results": "Results per page (10-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - }, - Required: []string{"id"}, - }, - { - Name: "x_hide_reply", Description: "Hide a reply to one of your tweets", - Parameters: map[string]string{"id": "Reply tweet ID to hide"}, - Required: []string{"id"}, - }, - { - Name: "x_unhide_reply", Description: "Unhide a previously hidden reply", - Parameters: map[string]string{"id": "Reply tweet ID to unhide"}, - Required: []string{"id"}, - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Timelines ────────────────────────────────────────────────── - { - Name: "x_get_user_tweets", Description: "Get tweets posted by a specific user. Use user_id from x_get_user or x_get_user_by_username", - Parameters: map[string]string{ - "user_id": "User ID (not username — use x_get_user_by_username to resolve)", - "max_results": "Results per page (5-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - "start_time": "Oldest UTC datetime (ISO 8601)", - "end_time": "Newest UTC datetime (ISO 8601)", - "exclude": "Comma-separated: retweets,replies", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_get_user_mentions", Description: "Get tweets mentioning a specific user", - Parameters: map[string]string{ - "user_id": "User ID", - "max_results": "Results per page (5-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - "start_time": "Oldest UTC datetime (ISO 8601)", - "end_time": "Newest UTC datetime (ISO 8601)", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_get_home_timeline", Description: "Get the authenticated user's home timeline (reverse chronological). Requires user context auth", - Parameters: map[string]string{ - "max_results": "Results per page (1-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - "start_time": "Oldest UTC datetime (ISO 8601)", - "end_time": "Newest UTC datetime (ISO 8601)", - "exclude": "Comma-separated: retweets,replies", - }, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Users ────────────────────────────────────────────────────── - { - Name: "x_get_user", Description: "Get a user by their numeric ID. Use x_get_user_by_username if you have a @handle instead", - Parameters: map[string]string{ - "id": "User ID", - "user_fields": "Comma-separated: id,name,username,created_at,description,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified", - }, - Required: []string{"id"}, - }, - { - Name: "x_get_user_by_username", Description: "Get a user by @username. Preferred entry point when you know the handle. Returns user ID needed for other endpoints", - Parameters: map[string]string{ - "username": "Username without @ prefix", - "user_fields": "Comma-separated user fields", - }, - Required: []string{"username"}, - }, - { - Name: "x_get_users", Description: "Get multiple users by IDs (up to 100)", - Parameters: map[string]string{ - "ids": "Comma-separated user IDs (max 100)", - "user_fields": "Comma-separated user fields", - }, - Required: []string{"ids"}, - }, - { - Name: "x_search_users", Description: "Search for users by name or username", - Parameters: map[string]string{ - "query": "Search query", - "max_results": "Results per page (1-100, default 10)", - "user_fields": "Comma-separated user fields", - }, - Required: []string{"query"}, - }, - { - Name: "x_get_me", Description: "Get the authenticated user's profile information", - Parameters: map[string]string{ - "user_fields": "Comma-separated user fields", - }, - }, - - // ── Follows ──────────────────────────────────────────────────── - { - Name: "x_get_following", Description: "Get users that a user follows", - Parameters: map[string]string{ - "user_id": "User ID", - "max_results": "Results per page (1-1000, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_get_followers", Description: "Get users who follow a user", - Parameters: map[string]string{ - "user_id": "User ID", - "max_results": "Results per page (1-1000, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_follow_user", Description: "Follow a user. Uses the authenticated user's ID automatically", - Parameters: map[string]string{"target_user_id": "User ID to follow"}, - Required: []string{"target_user_id"}, - }, - { - Name: "x_unfollow_user", Description: "Unfollow a user", - Parameters: map[string]string{"target_user_id": "User ID to unfollow"}, - Required: []string{"target_user_id"}, - }, - - // ── Blocks ───────────────────────────────────────────────────── - { - Name: "x_get_blocked", Description: "Get users blocked by the authenticated user", - Parameters: map[string]string{ - "max_results": "Results per page (1-1000, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - }, - { - Name: "x_block_user", Description: "Block a user", - Parameters: map[string]string{"target_user_id": "User ID to block"}, - Required: []string{"target_user_id"}, - }, - { - Name: "x_unblock_user", Description: "Unblock a user", - Parameters: map[string]string{"target_user_id": "User ID to unblock"}, - Required: []string{"target_user_id"}, - }, - - // ── Mutes ────────────────────────────────────────────────────── - { - Name: "x_get_muted", Description: "Get users muted by the authenticated user", - Parameters: map[string]string{ - "max_results": "Results per page (1-1000, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - }, - { - Name: "x_mute_user", Description: "Mute a user", - Parameters: map[string]string{"target_user_id": "User ID to mute"}, - Required: []string{"target_user_id"}, - }, - { - Name: "x_unmute_user", Description: "Unmute a user", - Parameters: map[string]string{"target_user_id": "User ID to unmute"}, - Required: []string{"target_user_id"}, - }, - - // ── Likes ────────────────────────────────────────────────────── - { - Name: "x_get_liking_users", Description: "Get users who liked a specific tweet", - Parameters: map[string]string{ - "tweet_id": "Tweet ID", - "max_results": "Results per page (1-100, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"tweet_id"}, - }, - { - Name: "x_get_liked_tweets", Description: "Get tweets liked by a specific user", - Parameters: map[string]string{ - "user_id": "User ID", - "max_results": "Results per page (10-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_like_tweet", Description: "Like a tweet", - Parameters: map[string]string{"tweet_id": "Tweet ID to like"}, - Required: []string{"tweet_id"}, - }, - { - Name: "x_unlike_tweet", Description: "Unlike a previously liked tweet", - Parameters: map[string]string{"tweet_id": "Tweet ID to unlike"}, - Required: []string{"tweet_id"}, - }, - - // ── Retweets ─────────────────────────────────────────────────── - { - Name: "x_get_retweeters", Description: "Get users who retweeted a specific tweet", - Parameters: map[string]string{ - "tweet_id": "Tweet ID", - "max_results": "Results per page (1-100, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"tweet_id"}, - }, - { - Name: "x_retweet", Description: "Retweet a tweet", - Parameters: map[string]string{"tweet_id": "Tweet ID to retweet"}, - Required: []string{"tweet_id"}, - }, - { - Name: "x_unretweet", Description: "Remove a retweet", - Parameters: map[string]string{"tweet_id": "Tweet ID to unretweet"}, - Required: []string{"tweet_id"}, - }, - - // ── Bookmarks ────────────────────────────────────────────────── - { - Name: "x_get_bookmarks", Description: "Get tweets bookmarked by the authenticated user", - Parameters: map[string]string{ - "max_results": "Results per page (1-100, default 10)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - }, - }, - { - Name: "x_bookmark_tweet", Description: "Bookmark a tweet", - Parameters: map[string]string{"tweet_id": "Tweet ID to bookmark"}, - Required: []string{"tweet_id"}, - }, - { - Name: "x_remove_bookmark", Description: "Remove a tweet from bookmarks", - Parameters: map[string]string{"tweet_id": "Tweet ID to remove from bookmarks"}, - Required: []string{"tweet_id"}, - }, - - // ── Lists ────────────────────────────────────────────────────── - { - Name: "x_get_list", Description: "Get a list by ID", - Parameters: map[string]string{ - "id": "List ID", - "list_fields": "Comma-separated: id,name,description,private,follower_count,member_count,owner_id,created_at", - }, - Required: []string{"id"}, - }, - { - Name: "x_get_owned_lists", Description: "Get lists owned by a user", - Parameters: map[string]string{ - "user_id": "User ID", - "max_results": "Results per page (1-100, default 100)", - "list_fields": "Comma-separated list fields", - "pagination_token": "Pagination token", - }, - Required: []string{"user_id"}, - }, - { - Name: "x_create_list", Description: "Create a new list", - Parameters: map[string]string{ - "name": "List name (1-25 chars)", - "description": "List description", - "private": "Whether list is private (true/false, default false)", - }, - Required: []string{"name"}, - }, - { - Name: "x_update_list", Description: "Update a list's name, description, or privacy setting", - Parameters: map[string]string{ - "id": "List ID", - "name": "New list name", - "description": "New list description", - "private": "Whether list is private (true/false)", - }, - Required: []string{"id"}, - }, - { - Name: "x_delete_list", Description: "Delete a list. Must be owned by the authenticated user", - Parameters: map[string]string{"id": "List ID to delete"}, - Required: []string{"id"}, - }, - { - Name: "x_get_list_tweets", Description: "Get tweets from a list's timeline", - Parameters: map[string]string{ - "id": "List ID", - "max_results": "Results per page (1-100, default 100)", - "tweet_fields": "Comma-separated tweet fields", - "pagination_token": "Pagination token", - }, - Required: []string{"id"}, - }, - { - Name: "x_get_list_members", Description: "Get members of a list", - Parameters: map[string]string{ - "id": "List ID", - "max_results": "Results per page (1-100, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"id"}, - }, - { - Name: "x_add_list_member", Description: "Add a user to a list. Must own the list", - Parameters: map[string]string{"id": "List ID", "user_id": "User ID to add"}, - Required: []string{"id", "user_id"}, - }, - { - Name: "x_remove_list_member", Description: "Remove a user from a list", - Parameters: map[string]string{"id": "List ID", "user_id": "User ID to remove"}, - Required: []string{"id", "user_id"}, - }, - { - Name: "x_get_list_followers", Description: "Get users who follow a list", - Parameters: map[string]string{ - "id": "List ID", - "max_results": "Results per page (1-100, default 100)", - "user_fields": "Comma-separated user fields", - "pagination_token": "Pagination token", - }, - Required: []string{"id"}, - }, - { - Name: "x_follow_list", Description: "Follow a list", - Parameters: map[string]string{"list_id": "List ID to follow"}, - Required: []string{"list_id"}, - }, - { - Name: "x_unfollow_list", Description: "Unfollow a list", - Parameters: map[string]string{"list_id": "List ID to unfollow"}, - Required: []string{"list_id"}, - }, - { - Name: "x_get_pinned_lists", Description: "Get lists pinned by the authenticated user", - Parameters: map[string]string{ - "list_fields": "Comma-separated list fields", - }, - }, - { - Name: "x_pin_list", Description: "Pin a list", - Parameters: map[string]string{"list_id": "List ID to pin"}, - Required: []string{"list_id"}, - }, - { - Name: "x_unpin_list", Description: "Unpin a list", - Parameters: map[string]string{"list_id": "List ID to unpin"}, - Required: []string{"list_id"}, - }, - - // ── Direct Messages ──────────────────────────────────────────── - { - Name: "x_list_dm_events", Description: "List recent DM events for the authenticated user. Rate limit: 15 requests per 15 minutes", - Parameters: map[string]string{ - "max_results": "Results per page (1-100, default 100)", - "dm_event_fields": "Comma-separated: id,text,event_type,dm_conversation_id,created_at,sender_id", - "pagination_token": "Pagination token", - }, - }, - { - Name: "x_get_dm_conversation", Description: "Get DM events with a specific user", - Parameters: map[string]string{ - "participant_id": "User ID of the other participant", - "max_results": "Results per page (1-100, default 100)", - "dm_event_fields": "Comma-separated DM event fields", - "pagination_token": "Pagination token", - }, - Required: []string{"participant_id"}, - }, - { - Name: "x_send_dm", Description: "Send a direct message to a user in an existing conversation", - Parameters: map[string]string{ - "participant_id": "User ID to send the DM to", - "text": "Message text", - }, - Required: []string{"participant_id", "text"}, - }, - { - Name: "x_create_dm_conversation", Description: "Create a new group DM conversation", - Parameters: map[string]string{ - "participant_ids": "Comma-separated user IDs to include", - "text": "Initial message text", - }, - Required: []string{"participant_ids", "text"}, - }, - - // ── Spaces ───────────────────────────────────────────────────── - { - Name: "x_get_space", Description: "Get details about a X Space by ID", - Parameters: map[string]string{ - "id": "Space ID", - "space_fields": "Comma-separated: id,title,state,host_ids,speaker_ids,participant_count,scheduled_start,created_at,lang", - }, - Required: []string{"id"}, - }, - { - Name: "x_search_spaces", Description: "Search for X Spaces by title", - Parameters: map[string]string{ - "query": "Search query for Space titles", - "state": "Filter by state: live, scheduled, or all (default all)", - "max_results": "Results per page (1-100, default 10)", - "space_fields": "Comma-separated space fields", - }, - Required: []string{"query"}, - }, - - // ── Usage ────────────────────────────────────────────────────── - { - Name: "x_get_usage", Description: "Get API usage statistics for tweet endpoints. Track your consumption", - Parameters: map[string]string{ - "days": "Number of days to look back (1-90, default 7)", - }, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/x/tools.yaml b/integrations/x/tools.yaml new file mode 100644 index 00000000..819f3cdf --- /dev/null +++ b/integrations/x/tools.yaml @@ -0,0 +1,619 @@ +version: 1 +tools: + x_get_tweet: + description: "Get a single tweet by ID. Returns full tweet object with text, author, metrics, and entities" + parameters: + id: + description: "Tweet ID" + required: true + tweet_fields: + description: "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text,conversation_id,reply_settings" + expansions: + description: "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys" + user_fields: + description: "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics" + + x_get_tweets: + description: "Get multiple tweets by IDs (up to 100). Start here for bulk tweet lookups" + parameters: + ids: + description: "Comma-separated tweet IDs (max 100)" + required: true + tweet_fields: + description: "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text" + expansions: + description: "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys" + user_fields: + description: "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics" + + x_create_tweet: + description: "Post a new tweet. Supports text, replies, quotes, and polls" + parameters: + text: + description: "Tweet text (up to 280 chars)" + required: true + reply_to: + description: "Tweet ID to reply to" + quote_tweet_id: + description: "Tweet ID to quote" + poll_options: + description: "Comma-separated poll options (2-4 choices)" + poll_duration_minutes: + description: "Poll duration in minutes (5-10080)" + + x_delete_tweet: + description: "Delete a tweet by ID. Must be authored by the authenticated user" + parameters: + id: + description: "Tweet ID to delete" + required: true + + x_search_recent: + description: "Search tweets from the last 7 days. Start here for most tweet discovery workflows. Supports the full X search query syntax" + parameters: + query: + description: "Search query (required). Supports operators: from:, to:, is:retweet, has:media, has:links, lang:, -is:retweet, etc." + required: true + max_results: + description: "Results per page (10-100, default 10)" + next_token: + description: "Pagination token from previous response" + tweet_fields: + description: "Comma-separated tweet fields: author_id,created_at,public_metrics,source,text,conversation_id,entities" + start_time: + description: "Oldest UTC datetime (ISO 8601: 2024-01-01T00:00:00Z)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + sort_order: + description: "Order of results: recency or relevancy" + + x_search_all: + description: "Full-archive search across all tweets (requires Pro tier or higher). Use x_search_recent for 7-day window instead" + parameters: + query: + description: "Search query (required). Same operators as x_search_recent" + required: true + max_results: + description: "Results per page (10-500, default 10)" + next_token: + description: "Pagination token from previous response" + tweet_fields: + description: "Comma-separated tweet fields" + start_time: + description: "Oldest UTC datetime (ISO 8601)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + sort_order: + description: "Order of results: recency or relevancy" + + x_get_tweet_count: + description: "Get count of tweets matching a search query from the last 7 days. Useful for analytics without retrieving full tweets" + parameters: + query: + description: "Search query" + required: true + granularity: + description: "Aggregation granularity: minute, hour, or day (default day)" + start_time: + description: "Oldest UTC datetime (ISO 8601)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + + x_get_quote_tweets: + description: "Get tweets that quote a specific tweet" + parameters: + id: + description: "Tweet ID to get quotes for" + required: true + max_results: + description: "Results per page (10-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + + x_hide_reply: + description: "Hide a reply to one of your tweets" + parameters: + id: + description: "Reply tweet ID to hide" + required: true + + x_unhide_reply: + description: "Unhide a previously hidden reply" + parameters: + id: + description: "Reply tweet ID to unhide" + required: true + + x_get_user_tweets: + description: "Get tweets posted by a specific user. Use user_id from x_get_user or x_get_user_by_username" + parameters: + user_id: + description: "User ID (not username — use x_get_user_by_username to resolve)" + required: true + max_results: + description: "Results per page (5-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + start_time: + description: "Oldest UTC datetime (ISO 8601)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + exclude: + description: "Comma-separated: retweets,replies" + + x_get_user_mentions: + description: "Get tweets mentioning a specific user" + parameters: + user_id: + description: "User ID" + required: true + max_results: + description: "Results per page (5-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + start_time: + description: "Oldest UTC datetime (ISO 8601)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + + x_get_home_timeline: + description: "Get the authenticated user's home timeline (reverse chronological). Requires user context auth" + parameters: + max_results: + description: "Results per page (1-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + start_time: + description: "Oldest UTC datetime (ISO 8601)" + end_time: + description: "Newest UTC datetime (ISO 8601)" + exclude: + description: "Comma-separated: retweets,replies" + + x_get_user: + description: "Get a user by their numeric ID. Use x_get_user_by_username if you have a @handle instead" + parameters: + id: + description: "User ID" + required: true + user_fields: + description: "Comma-separated: id,name,username,created_at,description,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified" + + x_get_user_by_username: + description: "Get a user by @username. Preferred entry point when you know the handle. Returns user ID needed for other endpoints" + parameters: + username: + description: "Username without @ prefix" + required: true + user_fields: + description: "Comma-separated user fields" + + x_get_users: + description: "Get multiple users by IDs (up to 100)" + parameters: + ids: + description: "Comma-separated user IDs (max 100)" + required: true + user_fields: + description: "Comma-separated user fields" + + x_search_users: + description: "Search for users by name or username" + parameters: + query: + description: "Search query" + required: true + max_results: + description: "Results per page (1-100, default 10)" + user_fields: + description: "Comma-separated user fields" + + x_get_me: + description: "Get the authenticated user's profile information" + parameters: + user_fields: + description: "Comma-separated user fields" + + x_get_following: + description: "Get users that a user follows" + parameters: + user_id: + description: "User ID" + required: true + max_results: + description: "Results per page (1-1000, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_get_followers: + description: "Get users who follow a user" + parameters: + user_id: + description: "User ID" + required: true + max_results: + description: "Results per page (1-1000, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_follow_user: + description: "Follow a user. Uses the authenticated user's ID automatically" + parameters: + target_user_id: + description: "User ID to follow" + required: true + + x_unfollow_user: + description: "Unfollow a user" + parameters: + target_user_id: + description: "User ID to unfollow" + required: true + + x_get_blocked: + description: "Get users blocked by the authenticated user" + parameters: + max_results: + description: "Results per page (1-1000, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_block_user: + description: "Block a user" + parameters: + target_user_id: + description: "User ID to block" + required: true + + x_unblock_user: + description: "Unblock a user" + parameters: + target_user_id: + description: "User ID to unblock" + required: true + + x_get_muted: + description: "Get users muted by the authenticated user" + parameters: + max_results: + description: "Results per page (1-1000, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_mute_user: + description: "Mute a user" + parameters: + target_user_id: + description: "User ID to mute" + required: true + + x_unmute_user: + description: "Unmute a user" + parameters: + target_user_id: + description: "User ID to unmute" + required: true + + x_get_liking_users: + description: "Get users who liked a specific tweet" + parameters: + tweet_id: + description: "Tweet ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_get_liked_tweets: + description: "Get tweets liked by a specific user" + parameters: + user_id: + description: "User ID" + required: true + max_results: + description: "Results per page (10-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + + x_like_tweet: + description: "Like a tweet" + parameters: + tweet_id: + description: "Tweet ID to like" + required: true + + x_unlike_tweet: + description: "Unlike a previously liked tweet" + parameters: + tweet_id: + description: "Tweet ID to unlike" + required: true + + x_get_retweeters: + description: "Get users who retweeted a specific tweet" + parameters: + tweet_id: + description: "Tweet ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_retweet: + description: "Retweet a tweet" + parameters: + tweet_id: + description: "Tweet ID to retweet" + required: true + + x_unretweet: + description: "Remove a retweet" + parameters: + tweet_id: + description: "Tweet ID to unretweet" + required: true + + x_get_bookmarks: + description: "Get tweets bookmarked by the authenticated user" + parameters: + max_results: + description: "Results per page (1-100, default 10)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + + x_bookmark_tweet: + description: "Bookmark a tweet" + parameters: + tweet_id: + description: "Tweet ID to bookmark" + required: true + + x_remove_bookmark: + description: "Remove a tweet from bookmarks" + parameters: + tweet_id: + description: "Tweet ID to remove from bookmarks" + required: true + + x_get_list: + description: "Get a list by ID" + parameters: + id: + description: "List ID" + required: true + list_fields: + description: "Comma-separated: id,name,description,private,follower_count,member_count,owner_id,created_at" + + x_get_owned_lists: + description: "Get lists owned by a user" + parameters: + user_id: + description: "User ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + list_fields: + description: "Comma-separated list fields" + pagination_token: + description: "Pagination token" + + x_create_list: + description: "Create a new list" + parameters: + name: + description: "List name (1-25 chars)" + required: true + description: + description: "List description" + private: + description: "Whether list is private (true/false, default false)" + + x_update_list: + description: "Update a list's name, description, or privacy setting" + parameters: + id: + description: "List ID" + required: true + name: + description: "New list name" + description: + description: "New list description" + private: + description: "Whether list is private (true/false)" + + x_delete_list: + description: "Delete a list. Must be owned by the authenticated user" + parameters: + id: + description: "List ID to delete" + required: true + + x_get_list_tweets: + description: "Get tweets from a list's timeline" + parameters: + id: + description: "List ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + tweet_fields: + description: "Comma-separated tweet fields" + pagination_token: + description: "Pagination token" + + x_get_list_members: + description: "Get members of a list" + parameters: + id: + description: "List ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_add_list_member: + description: "Add a user to a list. Must own the list" + parameters: + id: + description: "List ID" + required: true + user_id: + description: "User ID to add" + required: true + + x_remove_list_member: + description: "Remove a user from a list" + parameters: + id: + description: "List ID" + required: true + user_id: + description: "User ID to remove" + required: true + + x_get_list_followers: + description: "Get users who follow a list" + parameters: + id: + description: "List ID" + required: true + max_results: + description: "Results per page (1-100, default 100)" + user_fields: + description: "Comma-separated user fields" + pagination_token: + description: "Pagination token" + + x_follow_list: + description: "Follow a list" + parameters: + list_id: + description: "List ID to follow" + required: true + + x_unfollow_list: + description: "Unfollow a list" + parameters: + list_id: + description: "List ID to unfollow" + required: true + + x_get_pinned_lists: + description: "Get lists pinned by the authenticated user" + parameters: + list_fields: + description: "Comma-separated list fields" + + x_pin_list: + description: "Pin a list" + parameters: + list_id: + description: "List ID to pin" + required: true + + x_unpin_list: + description: "Unpin a list" + parameters: + list_id: + description: "List ID to unpin" + required: true + + x_list_dm_events: + description: "List recent DM events for the authenticated user. Rate limit: 15 requests per 15 minutes" + parameters: + max_results: + description: "Results per page (1-100, default 100)" + dm_event_fields: + description: "Comma-separated: id,text,event_type,dm_conversation_id,created_at,sender_id" + pagination_token: + description: "Pagination token" + + x_get_dm_conversation: + description: "Get DM events with a specific user" + parameters: + participant_id: + description: "User ID of the other participant" + required: true + max_results: + description: "Results per page (1-100, default 100)" + dm_event_fields: + description: "Comma-separated DM event fields" + pagination_token: + description: "Pagination token" + + x_send_dm: + description: "Send a direct message to a user in an existing conversation" + parameters: + participant_id: + description: "User ID to send the DM to" + required: true + text: + description: "Message text" + required: true + + x_create_dm_conversation: + description: "Create a new group DM conversation" + parameters: + participant_ids: + description: "Comma-separated user IDs to include" + required: true + text: + description: "Initial message text" + required: true + + x_get_space: + description: "Get details about a X Space by ID" + parameters: + id: + description: "Space ID" + required: true + space_fields: + description: "Comma-separated: id,title,state,host_ids,speaker_ids,participant_count,scheduled_start,created_at,lang" + + x_search_spaces: + description: "Search for X Spaces by title" + parameters: + query: + description: "Search query for Space titles" + required: true + state: + description: "Filter by state: live, scheduled, or all (default all)" + max_results: + description: "Results per page (1-100, default 10)" + space_fields: + description: "Comma-separated space fields" + + x_get_usage: + description: "Get API usage statistics for tweet endpoints. Track your consumption" + parameters: + days: + description: "Number of days to look back (1-90, default 7)" diff --git a/integrations/ynab/tools.go b/integrations/ynab/tools.go index c47666b7..8986bd35 100644 --- a/integrations/ynab/tools.go +++ b/integrations/ynab/tools.go @@ -1,284 +1,12 @@ package ynab -import mcp "github.com/daltoniam/switchboard" +import ( + _ "embed" -var tools = []mcp.ToolDefinition{ - // ── User ─────────────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_get_user"), Description: "Get the authenticated user's information", - }, + mcp "github.com/daltoniam/switchboard" +) - // ── Budgets ──────────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_budgets"), Description: "List all personal finance budgets the user has access to. Start here for budget, spending, and money management workflows.", - Parameters: map[string]string{"include_accounts": "Include account data (true/false)"}, - }, - { - Name: mcp.ToolName("ynab_get_budget"), Description: "Get a single budget with all related entities (accounts, categories, payees, transactions, etc.)", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_budget_settings"), Description: "Get budget settings including date and currency format", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, +//go:embed tools.yaml +var toolsYAML []byte - // ── Accounts ─────────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_accounts"), Description: "List all accounts in a budget", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_account"), Description: "Get a single account by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "account_id": "Account ID"}, - Required: []string{"account_id"}, - }, - { - Name: mcp.ToolName("ynab_create_account"), Description: "Create a new account in a budget", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "name": "Account name", - "type": "Account type: checking, savings, cash, creditCard, lineOfCredit, otherAsset, otherLiability, mortgage, autoLoan, studentLoan, personalLoan, medicalDebt, otherDebt", - "balance": "Starting balance in milliunits (1000 = $1.00)", - }, - Required: []string{"name", "type", "balance"}, - }, - - // ── Categories ───────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_categories"), Description: "List all category groups and their categories for a budget", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_category"), Description: "Get a single category by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "category_id": "Category ID"}, - Required: []string{"category_id"}, - }, - { - Name: mcp.ToolName("ynab_create_category"), Description: "Create a new category in a category group", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "name": "Category name", - "category_group_id": "Category group ID to add this category to", - }, - Required: []string{"name", "category_group_id"}, - }, - { - Name: mcp.ToolName("ynab_update_category"), Description: "Update a category's name or note", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "category_id": "Category ID", - "name": "New category name", - "note": "Category note", - }, - Required: []string{"category_id"}, - }, - { - Name: mcp.ToolName("ynab_get_month_category"), Description: "Get a category's budget amounts for a specific month", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "month": "Month in ISO date format (e.g. 2024-01-01) or 'current'", "category_id": "Category ID"}, - Required: []string{"month", "category_id"}, - }, - { - Name: mcp.ToolName("ynab_update_month_category"), Description: "Update the budgeted/assigned amount for a category in a specific month", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "month": "Month in ISO date format or 'current'", "category_id": "Category ID", "budgeted": "Assigned amount in milliunits (1000 = $1.00)"}, - Required: []string{"month", "category_id", "budgeted"}, - }, - - // ── Category Groups ──────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_create_category_group"), Description: "Create a new category group", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "name": "Category group name (max 50 chars)"}, - Required: []string{"name"}, - }, - { - Name: mcp.ToolName("ynab_update_category_group"), Description: "Update a category group's name", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "category_group_id": "Category group ID", "name": "New category group name (max 50 chars)"}, - Required: []string{"category_group_id", "name"}, - }, - - // ── Payees ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_payees"), Description: "List all payees in a budget", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_payee"), Description: "Get a single payee by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "payee_id": "Payee ID"}, - Required: []string{"payee_id"}, - }, - { - Name: mcp.ToolName("ynab_update_payee"), Description: "Update a payee's name", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "payee_id": "Payee ID", "name": "New payee name (max 500 chars)"}, - Required: []string{"payee_id", "name"}, - }, - - // ── Payee Locations ──────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_payee_locations"), Description: "List all payee locations (latitude/longitude) in a budget", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_payee_location"), Description: "Get a single payee location by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "payee_location_id": "Payee location ID"}, - Required: []string{"payee_location_id"}, - }, - { - Name: mcp.ToolName("ynab_list_locations_for_payee"), Description: "List all locations for a specific payee", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "payee_id": "Payee ID"}, - Required: []string{"payee_id"}, - }, - - // ── Months ───────────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_months"), Description: "List all budget months (summary of each month's budget status)", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_month"), Description: "Get a single budget month with category details", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "month": "Month in ISO date format (e.g. 2024-01-01) or 'current'"}, - Required: []string{"month"}, - }, - - // ── Transactions ─────────────────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_transactions"), Description: "List financial transactions (spending, expenses, purchases) for a budget, optionally filtered by date or type", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "since_date": "Only return transactions on or after this date (ISO format, e.g. 2024-01-01)", - "type": "Filter: uncategorized or unapproved", - }, - }, - { - Name: mcp.ToolName("ynab_get_transaction"), Description: "Get a single transaction by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "transaction_id": "Transaction ID"}, - Required: []string{"transaction_id"}, - }, - { - Name: mcp.ToolName("ynab_list_account_transactions"), Description: "List transactions for a specific account", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "account_id": "Account ID", - "since_date": "Only return transactions on or after this date (ISO format)", - "type": "Filter: uncategorized or unapproved", - }, - Required: []string{"account_id"}, - }, - { - Name: mcp.ToolName("ynab_list_category_transactions"), Description: "List transactions for a specific category", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "category_id": "Category ID", - "since_date": "Only return transactions on or after this date (ISO format)", - "type": "Filter: uncategorized or unapproved", - }, - Required: []string{"category_id"}, - }, - { - Name: mcp.ToolName("ynab_list_payee_transactions"), Description: "List transactions for a specific payee", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "payee_id": "Payee ID", - "since_date": "Only return transactions on or after this date (ISO format)", - "type": "Filter: uncategorized or unapproved", - }, - Required: []string{"payee_id"}, - }, - { - Name: mcp.ToolName("ynab_list_month_transactions"), Description: "List transactions for a specific month", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "month": "Month in ISO date format (e.g. 2024-01-01) or 'current'", - "since_date": "Only return transactions on or after this date (ISO format)", - "type": "Filter: uncategorized or unapproved", - }, - Required: []string{"month"}, - }, - { - Name: mcp.ToolName("ynab_create_transaction"), Description: "Create a new transaction. Amounts are in milliunits (1000 = $1.00). Outflows are negative.", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "account_id": "Account ID (required)", - "date": "Transaction date in ISO format (required, e.g. 2024-01-15)", - "amount": "Amount in milliunits (negative for outflows, e.g. -50000 = -$50.00)", - "payee_id": "Payee ID", - "payee_name": "Payee name (max 200 chars, creates new payee if payee_id not given)", - "category_id": "Category ID", - "memo": "Memo text (max 500 chars)", - "cleared": "Cleared status: cleared, uncleared, or reconciled", - "approved": "Whether transaction is approved (true/false)", - "flag_color": "Flag color: red, orange, yellow, green, blue, purple", - }, - Required: []string{"account_id", "date", "amount"}, - }, - { - Name: mcp.ToolName("ynab_update_transaction"), Description: "Update an existing transaction", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "transaction_id": "Transaction ID", - "account_id": "Account ID", - "date": "Transaction date in ISO format", - "amount": "Amount in milliunits", - "payee_id": "Payee ID", - "payee_name": "Payee name", - "category_id": "Category ID", - "memo": "Memo text", - "cleared": "Cleared status: cleared, uncleared, or reconciled", - "approved": "Whether transaction is approved (true/false)", - "flag_color": "Flag color: red, orange, yellow, green, blue, purple", - }, - Required: []string{"transaction_id"}, - }, - { - Name: mcp.ToolName("ynab_delete_transaction"), Description: "Delete a transaction", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "transaction_id": "Transaction ID"}, - Required: []string{"transaction_id"}, - }, - - // ── Scheduled Transactions ───────────────────────────────────── - { - Name: mcp.ToolName("ynab_list_scheduled_transactions"), Description: "List all scheduled (recurring) transactions for a budget", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)"}, - }, - { - Name: mcp.ToolName("ynab_get_scheduled_transaction"), Description: "Get a single scheduled transaction by ID", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "scheduled_transaction_id": "Scheduled transaction ID"}, - Required: []string{"scheduled_transaction_id"}, - }, - { - Name: mcp.ToolName("ynab_create_scheduled_transaction"), Description: "Create a new scheduled (recurring) transaction", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "account_id": "Account ID (required)", - "date": "First occurrence date in ISO format (required, max 5 years in future)", - "amount": "Amount in milliunits (negative for outflows)", - "frequency": "Recurrence: never, daily, weekly, everyOtherWeek, twiceAMonth, every4Weeks, monthly, everyOtherMonth, every3Months, every4Months, twiceAYear, yearly, everyOtherYear", - "payee_id": "Payee ID", - "payee_name": "Payee name (max 200 chars)", - "category_id": "Category ID", - "memo": "Memo text (max 500 chars)", - "flag_color": "Flag color: red, orange, yellow, green, blue, purple", - }, - Required: []string{"account_id", "date", "amount", "frequency"}, - }, - { - Name: mcp.ToolName("ynab_update_scheduled_transaction"), Description: "Update an existing scheduled transaction", - Parameters: map[string]string{ - "budget_id": "Budget ID (defaults to last-used)", - "scheduled_transaction_id": "Scheduled transaction ID", - "account_id": "Account ID", - "date": "First occurrence date in ISO format", - "amount": "Amount in milliunits", - "frequency": "Recurrence frequency", - "payee_id": "Payee ID", - "payee_name": "Payee name", - "category_id": "Category ID", - "memo": "Memo text", - "flag_color": "Flag color: red, orange, yellow, green, blue, purple", - }, - Required: []string{"scheduled_transaction_id"}, - }, - { - Name: mcp.ToolName("ynab_delete_scheduled_transaction"), Description: "Delete a scheduled transaction", - Parameters: map[string]string{"budget_id": "Budget ID (defaults to last-used)", "scheduled_transaction_id": "Scheduled transaction ID"}, - Required: []string{"scheduled_transaction_id"}, - }, -} +var tools = mcp.MustLoadToolsYAML(toolsYAML) diff --git a/integrations/ynab/tools.yaml b/integrations/ynab/tools.yaml new file mode 100644 index 00000000..553047a3 --- /dev/null +++ b/integrations/ynab/tools.yaml @@ -0,0 +1,423 @@ +version: 1 +tools: + ynab_get_user: + description: "Get the authenticated user's information" + + ynab_list_budgets: + description: "List all personal finance budgets the user has access to. Start here for budget, spending, and money management workflows." + parameters: + include_accounts: + description: "Include account data (true/false)" + + ynab_get_budget: + description: "Get a single budget with all related entities (accounts, categories, payees, transactions, etc.)" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_budget_settings: + description: "Get budget settings including date and currency format" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_list_accounts: + description: "List all accounts in a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_account: + description: "Get a single account by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + account_id: + description: "Account ID" + required: true + + ynab_create_account: + description: "Create a new account in a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + name: + description: "Account name" + required: true + type: + description: "Account type: checking, savings, cash, creditCard, lineOfCredit, otherAsset, otherLiability, mortgage, autoLoan, studentLoan, personalLoan, medicalDebt, otherDebt" + required: true + balance: + description: "Starting balance in milliunits (1000 = $1.00)" + required: true + + ynab_list_categories: + description: "List all category groups and their categories for a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_category: + description: "Get a single category by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + category_id: + description: "Category ID" + required: true + + ynab_create_category: + description: "Create a new category in a category group" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + name: + description: "Category name" + required: true + category_group_id: + description: "Category group ID to add this category to" + required: true + + ynab_update_category: + description: "Update a category's name or note" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + category_id: + description: "Category ID" + required: true + name: + description: "New category name" + note: + description: "Category note" + + ynab_get_month_category: + description: "Get a category's budget amounts for a specific month" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + month: + description: "Month in ISO date format (e.g. 2024-01-01) or 'current'" + required: true + category_id: + description: "Category ID" + required: true + + ynab_update_month_category: + description: "Update the budgeted/assigned amount for a category in a specific month" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + month: + description: "Month in ISO date format or 'current'" + required: true + category_id: + description: "Category ID" + required: true + budgeted: + description: "Assigned amount in milliunits (1000 = $1.00)" + required: true + + ynab_create_category_group: + description: "Create a new category group" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + name: + description: "Category group name (max 50 chars)" + required: true + + ynab_update_category_group: + description: "Update a category group's name" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + category_group_id: + description: "Category group ID" + required: true + name: + description: "New category group name (max 50 chars)" + required: true + + ynab_list_payees: + description: "List all payees in a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_payee: + description: "Get a single payee by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + payee_id: + description: "Payee ID" + required: true + + ynab_update_payee: + description: "Update a payee's name" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + payee_id: + description: "Payee ID" + required: true + name: + description: "New payee name (max 500 chars)" + required: true + + ynab_list_payee_locations: + description: "List all payee locations (latitude/longitude) in a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_payee_location: + description: "Get a single payee location by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + payee_location_id: + description: "Payee location ID" + required: true + + ynab_list_locations_for_payee: + description: "List all locations for a specific payee" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + payee_id: + description: "Payee ID" + required: true + + ynab_list_months: + description: "List all budget months (summary of each month's budget status)" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_month: + description: "Get a single budget month with category details" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + month: + description: "Month in ISO date format (e.g. 2024-01-01) or 'current'" + required: true + + ynab_list_transactions: + description: "List financial transactions (spending, expenses, purchases) for a budget, optionally filtered by date or type" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + since_date: + description: "Only return transactions on or after this date (ISO format, e.g. 2024-01-01)" + type: + description: "Filter: uncategorized or unapproved" + + ynab_get_transaction: + description: "Get a single transaction by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + transaction_id: + description: "Transaction ID" + required: true + + ynab_list_account_transactions: + description: "List transactions for a specific account" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + account_id: + description: "Account ID" + required: true + since_date: + description: "Only return transactions on or after this date (ISO format)" + type: + description: "Filter: uncategorized or unapproved" + + ynab_list_category_transactions: + description: "List transactions for a specific category" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + category_id: + description: "Category ID" + required: true + since_date: + description: "Only return transactions on or after this date (ISO format)" + type: + description: "Filter: uncategorized or unapproved" + + ynab_list_payee_transactions: + description: "List transactions for a specific payee" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + payee_id: + description: "Payee ID" + required: true + since_date: + description: "Only return transactions on or after this date (ISO format)" + type: + description: "Filter: uncategorized or unapproved" + + ynab_list_month_transactions: + description: "List transactions for a specific month" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + month: + description: "Month in ISO date format (e.g. 2024-01-01) or 'current'" + required: true + since_date: + description: "Only return transactions on or after this date (ISO format)" + type: + description: "Filter: uncategorized or unapproved" + + ynab_create_transaction: + description: "Create a new transaction. Amounts are in milliunits (1000 = $1.00). Outflows are negative." + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + account_id: + description: "Account ID (required)" + required: true + date: + description: "Transaction date in ISO format (required, e.g. 2024-01-15)" + required: true + amount: + description: "Amount in milliunits (negative for outflows, e.g. -50000 = -$50.00)" + required: true + payee_id: + description: "Payee ID" + payee_name: + description: "Payee name (max 200 chars, creates new payee if payee_id not given)" + category_id: + description: "Category ID" + memo: + description: "Memo text (max 500 chars)" + cleared: + description: "Cleared status: cleared, uncleared, or reconciled" + approved: + description: "Whether transaction is approved (true/false)" + flag_color: + description: "Flag color: red, orange, yellow, green, blue, purple" + + ynab_update_transaction: + description: "Update an existing transaction" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + transaction_id: + description: "Transaction ID" + required: true + account_id: + description: "Account ID" + date: + description: "Transaction date in ISO format" + amount: + description: "Amount in milliunits" + payee_id: + description: "Payee ID" + payee_name: + description: "Payee name" + category_id: + description: "Category ID" + memo: + description: "Memo text" + cleared: + description: "Cleared status: cleared, uncleared, or reconciled" + approved: + description: "Whether transaction is approved (true/false)" + flag_color: + description: "Flag color: red, orange, yellow, green, blue, purple" + + ynab_delete_transaction: + description: "Delete a transaction" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + transaction_id: + description: "Transaction ID" + required: true + + ynab_list_scheduled_transactions: + description: "List all scheduled (recurring) transactions for a budget" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + + ynab_get_scheduled_transaction: + description: "Get a single scheduled transaction by ID" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + scheduled_transaction_id: + description: "Scheduled transaction ID" + required: true + + ynab_create_scheduled_transaction: + description: "Create a new scheduled (recurring) transaction" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + account_id: + description: "Account ID (required)" + required: true + date: + description: "First occurrence date in ISO format (required, max 5 years in future)" + required: true + amount: + description: "Amount in milliunits (negative for outflows)" + required: true + frequency: + description: "Recurrence: never, daily, weekly, everyOtherWeek, twiceAMonth, every4Weeks, monthly, everyOtherMonth, every3Months, every4Months, twiceAYear, yearly, everyOtherYear" + required: true + payee_id: + description: "Payee ID" + payee_name: + description: "Payee name (max 200 chars)" + category_id: + description: "Category ID" + memo: + description: "Memo text (max 500 chars)" + flag_color: + description: "Flag color: red, orange, yellow, green, blue, purple" + + ynab_update_scheduled_transaction: + description: "Update an existing scheduled transaction" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + scheduled_transaction_id: + description: "Scheduled transaction ID" + required: true + account_id: + description: "Account ID" + date: + description: "First occurrence date in ISO format" + amount: + description: "Amount in milliunits" + frequency: + description: "Recurrence frequency" + payee_id: + description: "Payee ID" + payee_name: + description: "Payee name" + category_id: + description: "Category ID" + memo: + description: "Memo text" + flag_color: + description: "Flag color: red, orange, yellow, green, blue, purple" + + ynab_delete_scheduled_transaction: + description: "Delete a scheduled transaction" + parameters: + budget_id: + description: "Budget ID (defaults to last-used)" + scheduled_transaction_id: + description: "Scheduled transaction ID" + required: true diff --git a/mcp.go b/mcp.go index ef2dda0c..71f7f112 100644 --- a/mcp.go +++ b/mcp.go @@ -164,13 +164,72 @@ type Config struct { DollarsPerMTokInput float64 `json:"dollars_per_mtok_input,omitempty"` } +// ParamName identifies a parameter by name within a tool. +// Semantic type prevents mixing parameter names with arbitrary strings. +type ParamName string + +// Parameter describes a single parameter for a tool, including its name, +// description, and whether it is required. +type Parameter struct { + Name ParamName `json:"name"` + Description string `json:"description"` + Required bool `json:"required,omitempty"` +} + // ToolDefinition describes an API operation an integration exposes. // These are used by the search tool to let the AI discover available operations. type ToolDefinition struct { - Name ToolName `json:"name"` - Description string `json:"description"` - Parameters map[string]string `json:"parameters"` // param name -> description - Required []string `json:"required,omitempty"` + Name ToolName `json:"name"` + Description string `json:"description"` + Parameters []Parameter `json:"parameters"` +} + +// UnmarshalJSON implements backward-compatible deserialization for ToolDefinition. +// Handles the legacy map[string]string shape emitted by WASM guest binaries compiled +// against the pre-Phase-0 type, in addition to the new []Parameter array shape. +func (td *ToolDefinition) UnmarshalJSON(data []byte) error { + type wire struct { + Name ToolName `json:"name"` + Description string `json:"description"` + Parameters json.RawMessage `json:"parameters"` + Required []string `json:"required,omitempty"` // legacy field + } + var w wire + if err := json.Unmarshal(data, &w); err != nil { + return err + } + td.Name = w.Name + td.Description = w.Description + + if len(w.Parameters) == 0 { + return nil + } + + // Try new shape first: []Parameter + var params []Parameter + if err := json.Unmarshal(w.Parameters, ¶ms); err == nil { + td.Parameters = params + return nil + } + + // Fall back to legacy shape: map[string]string + Required []string + var legacyMap map[string]string + if err := json.Unmarshal(w.Parameters, &legacyMap); err != nil { + return err + } + requiredSet := make(map[string]bool, len(w.Required)) + for _, r := range w.Required { + requiredSet[r] = true + } + td.Parameters = make([]Parameter, 0, len(legacyMap)) + for name, desc := range legacyMap { + td.Parameters = append(td.Parameters, Parameter{ + Name: ParamName(name), + Description: desc, + Required: requiredSet[name], + }) + } + return nil } // ToolResult is the output of executing a tool. diff --git a/mcp_test.go b/mcp_test.go index 515319d5..f15d890c 100644 --- a/mcp_test.go +++ b/mcp_test.go @@ -1,6 +1,7 @@ package mcp import ( + "encoding/json" "errors" "fmt" "testing" @@ -120,24 +121,134 @@ func TestToolDefinition(t *testing.T) { td := ToolDefinition{ Name: ToolName("github_list_issues"), Description: "List issues", - Parameters: map[string]string{"owner": "Repo owner", "repo": "Repo name"}, - Required: []string{"owner", "repo"}, + Parameters: []Parameter{ + {Name: ParamName("owner"), Description: "Repo owner", Required: true}, + {Name: ParamName("repo"), Description: "Repo name", Required: true}, + }, } assert.Equal(t, ToolName("github_list_issues"), td.Name) assert.Equal(t, "List issues", td.Description) assert.Len(t, td.Parameters, 2) - assert.Equal(t, []string{"owner", "repo"}, td.Required) + assert.True(t, td.Parameters[0].Required) + assert.True(t, td.Parameters[1].Required) } func TestToolDefinitionOptionalRequired(t *testing.T) { td := ToolDefinition{ Name: ToolName("github_search_repos"), Description: "Search repos", - Parameters: map[string]string{"query": "Search query"}, + Parameters: []Parameter{{Name: ParamName("query"), Description: "Search query"}}, } - assert.Nil(t, td.Required) + assert.False(t, td.Parameters[0].Required) +} + +// TestToolDefinition_UnmarshalJSON exercises the backward-compat shape +// negotiation for WASM cached catalogs. The new shape (Parameters as +// []Parameter with per-param Required) and the legacy shape (Parameters as +// map[string]string + a top-level Required []string) must both decode into +// an equivalent ToolDefinition. The unmarshaler is the only consumer that +// sees the legacy shape and is otherwise untested. +func TestToolDefinition_UnmarshalJSON(t *testing.T) { + t.Run("new shape with required", func(t *testing.T) { + raw := `{ + "name": "github_get_repo", + "description": "Get a repository", + "parameters": [ + {"name": "owner", "description": "Repo owner", "required": true}, + {"name": "repo", "description": "Repo name", "required": true}, + {"name": "fields", "description": "Field projection"} + ] + }` + var td ToolDefinition + require.NoError(t, json.Unmarshal([]byte(raw), &td)) + assert.Equal(t, ToolName("github_get_repo"), td.Name) + assert.Equal(t, "Get a repository", td.Description) + require.Len(t, td.Parameters, 3) + assert.Equal(t, ParamName("owner"), td.Parameters[0].Name) + assert.True(t, td.Parameters[0].Required) + assert.True(t, td.Parameters[1].Required) + assert.False(t, td.Parameters[2].Required) + }) + + t.Run("legacy shape with required list", func(t *testing.T) { + raw := `{ + "name": "github_get_repo", + "description": "Get a repository", + "parameters": {"owner": "Repo owner", "repo": "Repo name", "fields": "Field projection"}, + "required": ["owner", "repo"] + }` + var td ToolDefinition + require.NoError(t, json.Unmarshal([]byte(raw), &td)) + assert.Equal(t, ToolName("github_get_repo"), td.Name) + assert.Equal(t, "Get a repository", td.Description) + require.Len(t, td.Parameters, 3) + + // Legacy shape decoding iterates a Go map, so Parameter order is not + // deterministic. Index by name and verify by lookup. + byName := map[ParamName]Parameter{} + for _, p := range td.Parameters { + byName[p.Name] = p + } + assert.Equal(t, "Repo owner", byName["owner"].Description) + assert.True(t, byName["owner"].Required) + assert.Equal(t, "Repo name", byName["repo"].Description) + assert.True(t, byName["repo"].Required) + assert.Equal(t, "Field projection", byName["fields"].Description) + assert.False(t, byName["fields"].Required) + }) + + t.Run("legacy shape without required list", func(t *testing.T) { + raw := `{ + "name": "tool", + "description": "desc", + "parameters": {"query": "Search query"} + }` + var td ToolDefinition + require.NoError(t, json.Unmarshal([]byte(raw), &td)) + require.Len(t, td.Parameters, 1) + assert.Equal(t, ParamName("query"), td.Parameters[0].Name) + assert.False(t, td.Parameters[0].Required) + }) + + t.Run("legacy required points to missing key", func(t *testing.T) { + // Required field lists a name that is not a key in the parameters map. + // The unmarshaler iterates the map only — phantom required names are + // silently dropped. Captures current behavior; if this changes the + // test will surface it. + raw := `{ + "name": "tool", + "description": "desc", + "parameters": {"query": "Search query"}, + "required": ["query", "phantom"] + }` + var td ToolDefinition + require.NoError(t, json.Unmarshal([]byte(raw), &td)) + require.Len(t, td.Parameters, 1) + assert.Equal(t, ParamName("query"), td.Parameters[0].Name) + assert.True(t, td.Parameters[0].Required) + }) + + t.Run("empty parameters", func(t *testing.T) { + raw := `{"name": "tool", "description": "desc"}` + var td ToolDefinition + require.NoError(t, json.Unmarshal([]byte(raw), &td)) + assert.Empty(t, td.Parameters) + }) + + t.Run("malformed json", func(t *testing.T) { + raw := `{"name": "tool", "description": "desc", "parameters": {` + var td ToolDefinition + assert.Error(t, json.Unmarshal([]byte(raw), &td)) + }) + + t.Run("parameters is invalid type", func(t *testing.T) { + // Not an array, not an object — should error after both paths fail. + raw := `{"name": "tool", "description": "desc", "parameters": 42}` + var td ToolDefinition + assert.Error(t, json.Unmarshal([]byte(raw), &td)) + }) } func TestToolResult(t *testing.T) { diff --git a/remotemcp/remotemcp.go b/remotemcp/remotemcp.go index a0898c12..036abb9a 100644 --- a/remotemcp/remotemcp.go +++ b/remotemcp/remotemcp.go @@ -194,29 +194,33 @@ func (r *remote) Execute(ctx context.Context, toolName mcp.ToolName, args map[st func convertTools(prefix string, tools []*mcpsdk.Tool) []mcp.ToolDefinition { var defs []mcp.ToolDefinition for _, t := range tools { - params := extractParams(t.InputSchema) - required := extractRequired(t.InputSchema) - defs = append(defs, mcp.ToolDefinition{ Name: mcp.ToolName(prefix + "_" + t.Name), Description: t.Description, - Parameters: params, - Required: required, + Parameters: extractParameters(t.InputSchema), }) } return defs } -func extractParams(schema any) map[string]string { - params := make(map[string]string) +func extractParameters(schema any) []mcp.Parameter { schemaMap, ok := toMap(schema) if !ok { - return params + return nil } props, ok := toMap(schemaMap["properties"]) if !ok { - return params + return nil + } + required := make(map[string]bool) + if reqArr, ok := schemaMap["required"].([]any); ok { + for _, v := range reqArr { + if s, ok := v.(string); ok { + required[s] = true + } + } } + var params []mcp.Parameter for k, v := range props { desc := "" if vMap, ok := toMap(v); ok { @@ -224,33 +228,15 @@ func extractParams(schema any) map[string]string { desc = d } } - params[k] = desc + params = append(params, mcp.Parameter{ + Name: mcp.ParamName(k), + Description: desc, + Required: required[k], + }) } return params } -func extractRequired(schema any) []string { - schemaMap, ok := toMap(schema) - if !ok { - return nil - } - req, ok := schemaMap["required"] - if !ok { - return nil - } - reqArr, ok := req.([]any) - if !ok { - return nil - } - var result []string - for _, v := range reqArr { - if s, ok := v.(string); ok { - result = append(result, s) - } - } - return result -} - func toMap(v any) (map[string]any, bool) { if m, ok := v.(map[string]any); ok { return m, true diff --git a/remotemcp/remotemcp_test.go b/remotemcp/remotemcp_test.go index 0bde4499..e5a2c40f 100644 --- a/remotemcp/remotemcp_test.go +++ b/remotemcp/remotemcp_test.go @@ -105,20 +105,24 @@ func TestConvertTools(t *testing.T) { err = json.Unmarshal(schemaJSON, &rawSchema) require.NoError(t, err) - params := extractParams(rawSchema) - assert.Equal(t, "Issue title", params["title"]) - assert.Equal(t, "Issue body", params["body"]) - - required := extractRequired(rawSchema) - assert.Equal(t, []string{"title"}, required) + // extractParameters now combines extractParams + extractRequired into []mcp.Parameter. + params := extractParameters(rawSchema) + paramMap := make(map[string]string, len(params)) + var requiredNames []string + for _, p := range params { + paramMap[string(p.Name)] = p.Description + if p.Required { + requiredNames = append(requiredNames, string(p.Name)) + } + } + assert.Equal(t, "Issue title", paramMap["title"]) + assert.Equal(t, "Issue body", paramMap["body"]) + assert.Equal(t, []string{"title"}, requiredNames) } func TestConvertTools_EmptySchema(t *testing.T) { - params := extractParams(nil) + params := extractParameters(nil) assert.Empty(t, params) - - required := extractRequired(nil) - assert.Nil(t, required) } func TestConvertResult_Nil(t *testing.T) { diff --git a/server/prompts/dynamic/circuit_breaker.md.tmpl b/server/prompts/dynamic/circuit_breaker.md.tmpl new file mode 100644 index 00000000..8d660a62 --- /dev/null +++ b/server/prompts/dynamic/circuit_breaker.md.tmpl @@ -0,0 +1,2 @@ +<%- /* circuit_breaker.md.tmpl — v1, no variants */ -%> +integration <% printf "%q" .Integration %> temporarily unavailable (circuit breaker open, try again in ~<% .CooldownSeconds %>s). Other integrations still work. diff --git a/server/prompts/dynamic/response_too_large_hint.md.tmpl b/server/prompts/dynamic/response_too_large_hint.md.tmpl new file mode 100644 index 00000000..5b28c1c1 --- /dev/null +++ b/server/prompts/dynamic/response_too_large_hint.md.tmpl @@ -0,0 +1,2 @@ +<%- /* response_too_large_hint.md.tmpl — v1, no variants */ -%> +narrow your query (e.g., add a filter, reduce page size, or request fewer fields) diff --git a/server/prompts/dynamic/search_hint_multi.md.tmpl b/server/prompts/dynamic/search_hint_multi.md.tmpl new file mode 100644 index 00000000..59c99b22 --- /dev/null +++ b/server/prompts/dynamic/search_hint_multi.md.tmpl @@ -0,0 +1,2 @@ +<%- /* search_hint_multi.md.tmpl — v1, no variants */ -%> +These tools span multiple integrations. Use execute with a script to chain them in a single call — intermediate results stay server-side and never enter the conversation. diff --git a/server/prompts/dynamic/search_hint_single.md.tmpl b/server/prompts/dynamic/search_hint_single.md.tmpl new file mode 100644 index 00000000..57da1769 --- /dev/null +++ b/server/prompts/dynamic/search_hint_single.md.tmpl @@ -0,0 +1,2 @@ +<%- /* search_hint_single.md.tmpl — v1, no variants */ -%> +Tip: if your task requires multiple tool calls, use execute with a script to chain them in a single call and reduce token usage. diff --git a/server/prompts/dynamic/search_summary.md.tmpl b/server/prompts/dynamic/search_summary.md.tmpl new file mode 100644 index 00000000..e2cb7622 --- /dev/null +++ b/server/prompts/dynamic/search_summary.md.tmpl @@ -0,0 +1,2 @@ +<%- /* search_summary.md.tmpl — v1, no variants */ -%> +Found <% .Total %> tools<% if .Query %> matching <% printf "%q" .Query %><% end %> diff --git a/server/prompts/embed.go b/server/prompts/embed.go new file mode 100644 index 00000000..f4d5d997 --- /dev/null +++ b/server/prompts/embed.go @@ -0,0 +1,34 @@ +package prompts + +import ( + "bytes" + "embed" + "strings" + "text/template" +) + +//go:embed dynamic/*.md.tmpl +var dynamicFS embed.FS + +//go:embed meta/*.md.tmpl +var metaFS embed.FS + +var dynamicTmpl = template.Must( + template.New("dynamic").Delims("<%", "%>").ParseFS(dynamicFS, "dynamic/*.md.tmpl"), +) + +var metaTmpl = template.Must( + template.New("meta").Delims("<%", "%>").ParseFS(metaFS, "meta/*.md.tmpl"), +) + +// Context carries request-scoped data for template authors. +// Empty in v1; add fields only when a concrete template needs them. +type Context struct{} + +func render(t *template.Template, name string, data any) string { + var buf bytes.Buffer + if err := t.ExecuteTemplate(&buf, name, data); err != nil { + panic(err) + } + return strings.TrimRight(buf.String(), "\n") +} diff --git a/server/prompts/meta.go b/server/prompts/meta.go new file mode 100644 index 00000000..e7c2cf26 --- /dev/null +++ b/server/prompts/meta.go @@ -0,0 +1,11 @@ +package prompts + +var Meta = metaAccessors{} + +type metaAccessors struct{} + +func (metaAccessors) Search() string { return render(metaTmpl, "search.md.tmpl", nil) } +func (metaAccessors) Execute() string { return render(metaTmpl, "execute.md.tmpl", nil) } +func (metaAccessors) Session() string { return render(metaTmpl, "session.md.tmpl", nil) } +func (metaAccessors) History() string { return render(metaTmpl, "history.md.tmpl", nil) } +func (metaAccessors) Pin() string { return render(metaTmpl, "pin.md.tmpl", nil) } diff --git a/server/prompts/meta/execute.md.tmpl b/server/prompts/meta/execute.md.tmpl new file mode 100644 index 00000000..fa8dac62 --- /dev/null +++ b/server/prompts/meta/execute.md.tmpl @@ -0,0 +1,65 @@ +<%- /* execute.md.tmpl — v1, no variants */ -%> +Execute a tool or run a JavaScript script that chains multiple tool calls. + +Two or more tool calls without a script means each intermediate result lands in the +conversation context — usually 5-10x the bytes you actually need. Use scripts to keep +intermediates server-side. + +If the second call doesn't read from the first, call them directly — script overhead +isn't worth it for two independent operations. + +Mode 1 — Script (provide script): + Scripts run in Goja, which implements ES5. Use var, function literals, and string concatenation. Goja does not support let, const, arrow functions, template literals, or destructuring — using them returns a parse error. + Call api.call(toolName, args) to invoke tools. Chain multiple calls, filter results, and return only what you need. + + {"script": "var issues = api.call('linear_search_issues', {query: 'BUG-1234'}); var email = issues[0].assignee.email; var user = api.call('postgres_execute_query', {query: 'SELECT * FROM users WHERE email = $1', params: [email]}); ({issue: issues[0], dbUser: user[0]});"} + +Mode 2 — Single tool (provide tool_name + arguments): + Use for one-off calls where scripting adds no benefit. + + {"tool_name": "github_list_issues", "arguments": {"owner": "golang", "repo": "go"}} + +Script API: + api.call(toolName, args[, opts]) — returns parsed JSON object. Use for data you need to read fields from (issues, PRs, metrics). + Optional opts: {fields: ["id", "title", "user.login"]} for server-side field projection. Dot-notation and brackets supported. + Throws on error (kills script). For partial-failure resilience, use tryCall. + api.tryCall(toolName, args[, opts]) — non-throwing call. Returns {ok: true, data: ...} or {ok: false, error: "..."}. + Also supports field projection. Prefer for cross-integration scripts where partial results are useful. + api.callRendered(toolName, args) — returns LLM-readable text (markdown or compacted JSON) instead of a JSON object. + The return value is a string — concatenation with `+` works; `.field` access throws. + Use for document content you need as readable text (pages, emails, issues). For field access, call `api.call()` instead — `callRendered` returns text, not an object. + No field projection. Throws on error. + api.tryCallRendered(toolName, args) — non-throwing callRendered. Returns {ok: true, data: "rendered string"} or {ok: false, error: "..."}. + The data field is a string — calling `JSON.parse()` on it throws because the value is already the final readable text, not serialized JSON. + console.log(...) — debug logging (included in output on error) + +When to use call vs callRendered: + Need .field access (data.id, result.items[0])? → api.call() + Need readable text to pass to another tool or return to user? → api.callRendered() + +Scripts can call integration tools (chain GitHub, Linear, Sentry, Datadog, Slack, etc.) but not the meta-tools (search, execute, session, history, pin). Run `search` before writing a script to discover the live tool names. + +List and search responses are automatically compacted to essential fields. +Use single-item get tools (e.g., github_get_issue) for full detail. +Responses over 50KB return an error — use filters, lower limit/per_page, or fetch individual items. +Script output is also capped at 50KB — return only the fields you need, not entire API responses. + +Script examples: + +Fetch a GitHub PR with field projection (only title, state, and branch refs returned): + {"script": "var pr = api.call('github_get_pull', {owner: 'o', repo: 'r', pull_number: 42}, {fields: ['title', 'state', 'body', 'base.ref', 'head.ref']}); var diff = api.call('github_get_pull_diff', {owner: 'o', repo: 'r', pull_number: 42}); ({pr: pr, diff: diff});"} + +Create a Linear issue then open a GitHub PR referencing it: + {"script": "var issue = api.call('linear_create_issue', {team_id: 'TEAM-ID', title: 'Fix auth bug', description: 'Details...'}); var pr = api.call('github_create_pull', {owner: 'o', repo: 'r', title: issue.identifier + ': ' + issue.title, head: 'fix-auth', base: 'main', body: 'Resolves ' + issue.url}); ({issue: issue.identifier, pr_url: pr.html_url});"} + +Look up a Sentry error, find the responsible deploy, and notify Slack: + {"script": "var issue = api.call('sentry_get_issue', {issue_id: '12345'}); var deploys = api.call('sentry_list_deploys', {organization_slug: 'org', version: issue.firstRelease.version}); api.call('slack_post_message', {channel: '#alerts', text: 'Sentry issue ' + issue.title + ' introduced in deploy ' + deploys[0].environment}); ({sentry: issue.shortId, deploy: deploys[0].environment});"} + +Cross-integration correlation with tryCall and field projection: + {"script": "var pr = api.call('github_get_pull', {owner: 'o', repo: 'r', pull_number: 42}, {fields: ['title', 'state']}); var linear = api.tryCall('linear_search_issues', {query: pr.title}, {fields: ['issues.nodes[].identifier', 'issues.nodes[].title']}); ({pr: pr, linear: linear.ok ? linear.data : {error: linear.error}});"} + +List issues with server-side projection (only id, title, labels — no manual .map() needed): + {"script": "api.call('github_list_issues', {owner: 'o', repo: 'r', state: 'open'}, {fields: ['number', 'title', 'labels[].name']});"} + +Pipe rendered document content between tools (callRendered returns readable text, not raw JSON): + {"script": "var page = api.callRendered('notion_get_page_content', {page_id: 'abc'}); var summary = api.call('ollama_chat', {model: 'gemma3', messages: [{role: 'user', content: 'Summarize:\\n' + page}]}); ({summary: summary.message.content});"} diff --git a/server/prompts/meta/history.md.tmpl b/server/prompts/meta/history.md.tmpl new file mode 100644 index 00000000..ef5a5c5a --- /dev/null +++ b/server/prompts/meta/history.md.tmpl @@ -0,0 +1,5 @@ +<%- /* history.md.tmpl — v1, no variants */ -%> +Retrieve a compact log of tool calls made in this session. + +The call log survives context compression — query it instead of re-running tools to recover what was already fetched. +Returns: [{seq, tool, args, summary, is_error, timestamp}] ordered by time. diff --git a/server/prompts/meta/pin.md.tmpl b/server/prompts/meta/pin.md.tmpl new file mode 100644 index 00000000..9429be48 --- /dev/null +++ b/server/prompts/meta/pin.md.tmpl @@ -0,0 +1,8 @@ +<%- /* pin.md.tmpl — v1, no variants */ -%> +Manage pinned results from previous execute calls. + +Every successful execute auto-pins its result with a handle ($1, $2, ...). +Use handles in execute arguments to reference previous results without re-fetching: + execute({tool_name: "github_get_issue", arguments: {owner: "$1.owner.login", issue_number: "$2.number"}}) + +The field path after the handle follows the shape the source tool returned — in the example above, $1 was a GitHub issue, so $1.owner.login is the issue author. For a Linear issue, $1.assignee.email; for a database row, $1.id. diff --git a/server/prompts/meta/search.md.tmpl b/server/prompts/meta/search.md.tmpl new file mode 100644 index 00000000..3b14739c --- /dev/null +++ b/server/prompts/meta/search.md.tmpl @@ -0,0 +1,10 @@ +<%- /* search.md.tmpl — v1, no variants */ -%> +Search available tools across all integrations (GitHub, Datadog, Linear, Sentry, Slack, etc.). + +Tool names shift across integration versions. A guessed name returns "tool not found" with no fallback — search first to get the live name. + +Query format — use 1-2 keywords, not full sentences. Fewer words = better results: +- {"query": "create ticket"} — synonym matching finds linear_create_issue +- {"query": "slack send"} — integration name + verb is ideal +- {"integration": "sentry", "query": "errors"} — or use the integration filter +Avoid 4+ word queries — they return fewer results. Search twice with short queries instead of once with a long query. diff --git a/server/prompts/meta/session.md.tmpl b/server/prompts/meta/session.md.tmpl new file mode 100644 index 00000000..b7536141 --- /dev/null +++ b/server/prompts/meta/session.md.tmpl @@ -0,0 +1,8 @@ +<%- /* session.md.tmpl — v1, no variants */ -%> +Manage session-scoped context to avoid repeating parameters. + +Set context once (e.g., owner/repo) and all subsequent execute calls auto-inject those values as defaults. +Explicit arguments always override session context. + +Example: {"action": "set", "context": {"owner": "daltoniam", "repo": "switchboard"}} +Then: execute({tool_name: "github_list_issues", arguments: {state: "open"}}) — owner/repo injected automatically. diff --git a/server/prompts/prompts_test.go b/server/prompts/prompts_test.go new file mode 100644 index 00000000..c8cce704 --- /dev/null +++ b/server/prompts/prompts_test.go @@ -0,0 +1,65 @@ +package prompts_test + +import ( + "testing" + + "github.com/daltoniam/switchboard/server/prompts" + "github.com/stretchr/testify/require" +) + +func TestSearchHintMulti(t *testing.T) { + want := "These tools span multiple integrations. Use execute with a script to chain them in a single call — intermediate results stay server-side and never enter the conversation." + require.Equal(t, want, prompts.SearchHintMulti(prompts.Context{})) +} + +func TestSearchHintSingle(t *testing.T) { + want := "Tip: if your task requires multiple tool calls, use execute with a script to chain them in a single call and reduce token usage." + require.Equal(t, want, prompts.SearchHintSingle(prompts.Context{})) +} + +func TestResponseTooLargeHint(t *testing.T) { + want := "narrow your query (e.g., add a filter, reduce page size, or request fewer fields)" + require.Equal(t, want, prompts.ResponseTooLargeHint(prompts.Context{})) +} + +func TestCircuitBreaker(t *testing.T) { + cases := []struct { + name string + integration string + cooldownSeconds int + want string + }{ + {"typical", "github", 30, `integration "github" temporarily unavailable (circuit breaker open, try again in ~30s). Other integrations still work.`}, + {"empty name", "", 30, `integration "" temporarily unavailable (circuit breaker open, try again in ~30s). Other integrations still work.`}, + {"zero cooldown", "linear", 0, `integration "linear" temporarily unavailable (circuit breaker open, try again in ~0s). Other integrations still work.`}, + {"large cooldown", "datadog", 3600, `integration "datadog" temporarily unavailable (circuit breaker open, try again in ~3600s). Other integrations still work.`}, + {"name with quotes", `a"b`, 5, `integration "a\"b" temporarily unavailable (circuit breaker open, try again in ~5s). Other integrations still work.`}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.want, prompts.CircuitBreaker(prompts.Context{}, tc.integration, tc.cooldownSeconds)) + }) + } +} + +func TestSearchSummary(t *testing.T) { + cases := []struct { + name string + total int + query string + want string + }{ + {"empty", 0, "", "Found 0 tools"}, + {"no query", 5, "", "Found 5 tools"}, + {"with query", 12, "auth", `Found 12 tools matching "auth"`}, + {"unicode", 3, "café", `Found 3 tools matching "café"`}, + {"japanese", 0, "日本", `Found 0 tools matching "日本"`}, + {"query with quotes", 1, `a"b`, `Found 1 tools matching "a\"b"`}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := prompts.SearchSummary(prompts.Context{}, tc.total, tc.query) + require.Equal(t, tc.want, got) + }) + } +} diff --git a/server/prompts/render_test.go b/server/prompts/render_test.go new file mode 100644 index 00000000..680fe717 --- /dev/null +++ b/server/prompts/render_test.go @@ -0,0 +1,71 @@ +package prompts + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// render() must strip exactly one trailing newline so embedded .md.tmpl files +// (which end in \n by editor default) produce strings byte-equal to what the +// SDK would have received from a Go raw-string literal. Tested through every +// Meta accessor — also verifies each accessor's template name in meta.go +// resolves against a real file. +func TestRender_TrimsTrailingNewline(t *testing.T) { + cases := []struct { + name string + fn func() string + }{ + {"Meta.Search", Meta.Search}, + {"Meta.Execute", Meta.Execute}, + {"Meta.Session", Meta.Session}, + {"Meta.History", Meta.History}, + {"Meta.Pin", Meta.Pin}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := tc.fn() + require.NotEmpty(t, got) + require.False(t, strings.HasSuffix(got, "\n"), + "render() left trailing newline in %s output", tc.name) + }) + } +} + +// render() must fail loudly on a missing template. A silent empty-string +// return would mean a typo in an accessor's template name produces a blank +// Tool.Description at the wire boundary with no compile-time or test-time +// signal — this is the "total seam stays loud" guarantee. +func TestRender_PanicsOnMissingTemplate(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic on missing template, got nil") + } + }() + render(metaTmpl, "nonexistent.md.tmpl", nil) +} + +// execute.md.tmpl contains 9 literal "}}" sequences inside nested JSON +// examples. With default template delims "{{" "}}", init would have panicked +// and the package wouldn't have loaded. Custom "<%" "%>" delims are +// load-bearing for this codebase; this test confirms they pass "}}" through +// to the output verbatim. +func TestMetaTmpl_CustomDelimsHandleNestedBraces(t *testing.T) { + got := render(metaTmpl, "execute.md.tmpl", nil) + require.Contains(t, got, "}}", + "execute.md.tmpl should contain literal }} from JSON examples; custom delims must be active") +} + +// metaTmpl and dynamicTmpl are separate parsers backed by separate embed.FS +// values. A template name that exists only in dynamic/ must not resolve via +// metaTmpl — a future file in meta/ sharing a base name with one in dynamic/ +// would otherwise collide silently and cross-link the namespaces. +func TestParsers_AreIsolated(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Fatal("expected panic when looking up dynamic template via metaTmpl") + } + }() + render(metaTmpl, "search_summary.md.tmpl", nil) +} diff --git a/server/prompts/wrappers.go b/server/prompts/wrappers.go new file mode 100644 index 00000000..3c8a1659 --- /dev/null +++ b/server/prompts/wrappers.go @@ -0,0 +1,29 @@ +package prompts + +func SearchSummary(ctx Context, total int, query string) string { + return render(dynamicTmpl, "search_summary.md.tmpl", struct { + Ctx Context + Total int + Query string + }{ctx, total, query}) +} + +func SearchHintMulti(ctx Context) string { + return render(dynamicTmpl, "search_hint_multi.md.tmpl", struct{ Ctx Context }{ctx}) +} + +func SearchHintSingle(ctx Context) string { + return render(dynamicTmpl, "search_hint_single.md.tmpl", struct{ Ctx Context }{ctx}) +} + +func ResponseTooLargeHint(ctx Context) string { + return render(dynamicTmpl, "response_too_large_hint.md.tmpl", struct{ Ctx Context }{ctx}) +} + +func CircuitBreaker(ctx Context, integration string, cooldownSeconds int) string { + return render(dynamicTmpl, "circuit_breaker.md.tmpl", struct { + Ctx Context + Integration string + CooldownSeconds int + }{ctx, integration, cooldownSeconds}) +} diff --git a/server/search.go b/server/search.go index 90a92c6a..01526e5c 100644 --- a/server/search.go +++ b/server/search.go @@ -76,34 +76,42 @@ type SearchIndex struct { } // toToolInfo converts a scored result to a search response entry. -// Copies parameters to avoid mutating the original tool definition. +// Clones parameters to avoid mutating the original tool definition when +// extractSharedParameters filters in place. Required is snapshotted from +// the source parameters so the wire's required[] survives that filtering. func toToolInfo(r scoredResult) searchToolInfo { - params := make(map[string]string, len(r.Tool.Parameters)) - for k, v := range r.Tool.Parameters { - params[k] = v - } return searchToolInfo{ Integration: r.Integration, Name: string(r.Tool.Name), Description: r.Tool.Description, - Parameters: params, - Required: r.Tool.Required, + Parameters: slices.Clone(r.Tool.Parameters), + Required: requiredNames(r.Tool.Parameters), } } // toolDefToInfo converts a raw tool definition to a search response entry. +// Required is snapshotted from the source parameters; see toToolInfo. func toolDefToInfo(integration string, tool mcp.ToolDefinition) searchToolInfo { - params := make(map[string]string, len(tool.Parameters)) - for k, v := range tool.Parameters { - params[k] = v - } return searchToolInfo{ Integration: integration, Name: string(tool.Name), Description: tool.Description, - Parameters: params, - Required: tool.Required, + Parameters: slices.Clone(tool.Parameters), + Required: requiredNames(tool.Parameters), + } +} + +// requiredNames returns the names of parameters with Required:true. Captured +// once at searchToolInfo construction so the wire's required[] is independent +// of subsequent filtering by extractSharedParameters. +func requiredNames(params []mcp.Parameter) []string { + var out []string + for _, p := range params { + if p.Required { + out = append(out, string(p.Name)) + } } + return out } // synonymGroups defines equivalence sets of words that should match diff --git a/server/server.go b/server/server.go index 9d1bf3f3..a6ec6ee0 100644 --- a/server/server.go +++ b/server/server.go @@ -19,6 +19,7 @@ import ( mcp "github.com/daltoniam/switchboard" "github.com/daltoniam/switchboard/compact" "github.com/daltoniam/switchboard/script" + "github.com/daltoniam/switchboard/server/prompts" "github.com/daltoniam/switchboard/version" mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -47,13 +48,62 @@ func responseLimitFor(integration mcp.Integration, toolName mcp.ToolName) int { } // searchToolInfo represents a tool in search results. +// +// Required is parsed once at construction (toToolInfo / toolDefToInfo) from +// the source ToolDefinition's Parameters. The field is the parse-don't-validate +// proof of which parameters this tool requires — downstream code never +// re-derives required-ness from the Parameters slice. extractSharedParameters +// mutates Parameters (filtering shared params out for the wire response) but +// MUST NOT touch Required: the wire's required[] is the snapshot, not a +// derivation from the mutated slice. +// +// Permanent wire-shape contract: this type's JSON shape is documented in the +// meta-tool description. There is no inverse deserializer — the LLM consumes +// it but does not round-trip back into a searchToolInfo. Do not reconstruct +// from the wire shape. type searchToolInfo struct { - Integration string `json:"integration"` - Name string `json:"name"` - Description string `json:"description"` - Parameters map[string]string `json:"parameters"` - Required []string `json:"required,omitempty"` - Configured *bool `json:"configured,omitempty"` // nil = omitted (configured); false = not yet configured + Integration string `json:"integration"` + Name string `json:"name"` + Description string `json:"description"` + Parameters []mcp.Parameter `json:"parameters"` + Required []string `json:"required,omitempty"` // snapshotted at construction; never re-derived + Configured *bool `json:"configured,omitempty"` // nil = omitted (configured); false = not yet configured +} + +// MarshalJSON preserves the search-response wire format. Parameters serialize +// as a JSON object {name: description}, required as a separate sorted array — +// the shape LLM consumers of the search meta-tool depend on. Internal +// processing uses []mcp.Parameter for type-system consistency with +// ToolDefinition; this method bridges the two. +// +// Required is emitted from the snapshot field, not re-derived from Parameters. +// This preserves required-ness for parameters that extractSharedParameters has +// moved to shared_parameters (where the map[string]string wire shape would +// otherwise drop the required flag). +func (s searchToolInfo) MarshalJSON() ([]byte, error) { + params := make(map[string]string, len(s.Parameters)) + for _, p := range s.Parameters { + params[string(p.Name)] = p.Description + } + required := slices.Clone(s.Required) + sort.Strings(required) + + type wire struct { + Integration string `json:"integration"` + Name string `json:"name"` + Description string `json:"description"` + Parameters map[string]string `json:"parameters"` + Required []string `json:"required,omitempty"` + Configured *bool `json:"configured,omitempty"` + } + return json.Marshal(wire{ + Integration: s.Integration, + Name: s.Name, + Description: s.Description, + Parameters: params, + Required: required, + Configured: s.Configured, + }) } // searchableIntegration pairs an integration with its name for iteration. @@ -153,16 +203,8 @@ func (s *Server) registerTools() { s.buildSearchIndex() searchTool := &mcpsdk.Tool{ - Name: "search", - Description: `Search available tools across all integrations (GitHub, Datadog, Linear, Sentry, Slack, etc.). - -IMPORTANT: Always search before calling execute. Do NOT guess tool names. - -Query format — use 1-2 keywords, not full sentences. Fewer words = better results: -- {"query": "create ticket"} — synonym matching finds linear_create_issue -- {"query": "slack send"} — integration name + verb is ideal -- {"integration": "sentry", "query": "errors"} — or use the integration filter -Avoid 4+ word queries — they return fewer results. Search twice with short queries instead of once with a long query.`, + Name: "search", + Description: prompts.Meta.Search(), InputSchema: objectSchema(map[string]any{ "query": map[string]any{ "type": "string", @@ -184,68 +226,8 @@ Avoid 4+ word queries — they return fewer results. Search twice with short que } executeTool := &mcpsdk.Tool{ - Name: "execute", - Description: `Execute a tool or run a JavaScript script that chains multiple tool calls. - -PREFER scripts when a task requires 2+ tool calls or crosses integrations — intermediate -results stay server-side and never enter the conversation, saving tokens dramatically. - -Mode 1 — Script (provide script): - Write ES5 JavaScript (var, function(){}, string + concatenation). No let/const, arrow functions, template literals, or destructuring. - Call api.call(toolName, args) to invoke tools. Chain multiple calls, filter results, and return only what you need. - - {"script": "var issues = api.call('linear_search_issues', {query: 'BUG-1234'}); var email = issues[0].assignee.email; var user = api.call('postgres_execute_query', {query: 'SELECT * FROM users WHERE email = $1', params: [email]}); ({issue: issues[0], dbUser: user[0]});"} - -Mode 2 — Single tool (provide tool_name + arguments): - Use for one-off calls where scripting adds no benefit. - - {"tool_name": "github_list_issues", "arguments": {"owner": "golang", "repo": "go"}} - -Script API: - api.call(toolName, args[, opts]) — returns parsed JSON object. Use for data you need to read fields from (issues, PRs, metrics). - Optional opts: {fields: ["id", "title", "user.login"]} for server-side field projection. Dot-notation and brackets supported. - Throws on error (kills script). For partial-failure resilience, use tryCall. - api.tryCall(toolName, args[, opts]) — non-throwing call. Returns {ok: true, data: ...} or {ok: false, error: "..."}. - Also supports field projection. Prefer for cross-integration scripts where partial results are useful. - api.callRendered(toolName, args) — returns LLM-readable STRING (markdown or compacted JSON) instead of a JSON object. - The return value is a STRING, not an object — use string concatenation (+), not .field access. - Use for document content you need as readable text (pages, emails, issues). Do NOT use when you need field access. - No field projection. Throws on error. - api.tryCallRendered(toolName, args) — non-throwing callRendered. Returns {ok: true, data: "rendered string"} or {ok: false, error: "..."}. - The data value is a STRING. Do not JSON.parse() it — it is already the final readable text. - console.log(...) — debug logging (included in output on error) - -When to use call vs callRendered: - Need .field access (data.id, result.items[0])? → api.call() - Need readable text to pass to another tool or return to user? → api.callRendered() - -Scripts can call integration tools — chain GitHub, Linear, Sentry, Datadog, Slack, etc. in one script. -Scripts CANNOT call the search or execute meta-tools. Use search before writing a script to discover tool names. - -List and search responses are automatically compacted to essential fields. -Use single-item get tools (e.g., github_get_issue) for full detail. -Responses over 50KB return an error — use filters, lower limit/per_page, or fetch individual items. -Script output is also capped at 50KB — return only the fields you need, not entire API responses. - -Script examples: - -Fetch a GitHub PR with field projection (only title, state, and branch refs returned): - {"script": "var pr = api.call('github_get_pull', {owner: 'o', repo: 'r', pull_number: 42}, {fields: ['title', 'state', 'body', 'base.ref', 'head.ref']}); var diff = api.call('github_get_pull_diff', {owner: 'o', repo: 'r', pull_number: 42}); ({pr: pr, diff: diff});"} - -Create a Linear issue then open a GitHub PR referencing it: - {"script": "var issue = api.call('linear_create_issue', {team_id: 'TEAM-ID', title: 'Fix auth bug', description: 'Details...'}); var pr = api.call('github_create_pull', {owner: 'o', repo: 'r', title: issue.identifier + ': ' + issue.title, head: 'fix-auth', base: 'main', body: 'Resolves ' + issue.url}); ({issue: issue.identifier, pr_url: pr.html_url});"} - -Look up a Sentry error, find the responsible deploy, and notify Slack: - {"script": "var issue = api.call('sentry_get_issue', {issue_id: '12345'}); var deploys = api.call('sentry_list_deploys', {organization_slug: 'org', version: issue.firstRelease.version}); api.call('slack_post_message', {channel: '#alerts', text: 'Sentry issue ' + issue.title + ' introduced in deploy ' + deploys[0].environment}); ({sentry: issue.shortId, deploy: deploys[0].environment});"} - -Cross-integration correlation with tryCall and field projection: - {"script": "var pr = api.call('github_get_pull', {owner: 'o', repo: 'r', pull_number: 42}, {fields: ['title', 'state']}); var linear = api.tryCall('linear_search_issues', {query: pr.title}, {fields: ['issues.nodes[].identifier', 'issues.nodes[].title']}); ({pr: pr, linear: linear.ok ? linear.data : {error: linear.error}});"} - -List issues with server-side projection (only id, title, labels — no manual .map() needed): - {"script": "api.call('github_list_issues', {owner: 'o', repo: 'r', state: 'open'}, {fields: ['number', 'title', 'labels[].name']});"} - -Pipe rendered document content between tools (callRendered returns readable text, not raw JSON): - {"script": "var page = api.callRendered('notion_get_page_content', {page_id: 'abc'}); var summary = api.call('ollama_chat', {model: 'gemma3', messages: [{role: 'user', content: 'Summarize:\\n' + page}]}); ({summary: summary.message.content});"}`, + Name: "execute", + Description: prompts.Meta.Execute(), InputSchema: objectSchema(map[string]any{ "tool_name": map[string]any{ "type": "string", @@ -263,23 +245,12 @@ Pipe rendered document content between tools (callRendered returns readable text } sessionTool := &mcpsdk.Tool{ - Name: "session", - Description: `Manage session-scoped context to avoid repeating parameters. - -Set context once (e.g., owner/repo) and all subsequent execute calls auto-inject those values as defaults. -Explicit arguments always override session context. - -Actions: -- "set": Upsert key-value pairs into session context -- "get": Return current session context -- "clear": Reset session context to empty - -Example: {"action": "set", "context": {"owner": "daltoniam", "repo": "switchboard"}} -Then: execute({tool_name: "github_list_issues", arguments: {state: "open"}}) — owner/repo injected automatically.`, + Name: "session", + Description: prompts.Meta.Session(), InputSchema: objectSchema(map[string]any{ "action": map[string]any{ "type": "string", - "description": `The action to perform: "set", "get", or "clear".`, + "description": `"set" upserts key-value pairs into session context; "get" returns the current context; "clear" resets context to empty.`, "enum": []string{"set", "get", "clear"}, }, "context": map[string]any{ @@ -290,11 +261,8 @@ Then: execute({tool_name: "github_list_issues", arguments: {state: "open"}}) — } historyTool := &mcpsdk.Tool{ - Name: "history", - Description: `Retrieve a compact log of tool calls made in this session. - -Useful after context compression to recover what was already fetched without re-executing. -Returns: [{seq, tool, args, summary, is_error, timestamp}] ordered by time.`, + Name: "history", + Description: prompts.Meta.History(), InputSchema: objectSchema(map[string]any{ "last_n": map[string]any{ "type": "integer", @@ -308,21 +276,12 @@ Returns: [{seq, tool, args, summary, is_error, timestamp}] ordered by time.`, } pinTool := &mcpsdk.Tool{ - Name: "pin", - Description: `Manage pinned results from previous execute calls. - -Every successful execute auto-pins its result with a handle ($1, $2, ...). -Use handles in execute arguments to reference previous results without re-fetching: - execute({tool_name: "github_get_issue", arguments: {owner: "$1.owner.login", issue_number: "$2.number"}}) - -Actions: -- "list": Show all pinned handles with tool name and size -- "get": Retrieve a pinned result by handle, optionally extracting a sub-field via path -- "unpin": Free memory by removing a pinned result`, + Name: "pin", + Description: prompts.Meta.Pin(), InputSchema: objectSchema(map[string]any{ "action": map[string]any{ "type": "string", - "description": `The action to perform: "list", "get", or "unpin".`, + "description": `"list" shows all pinned handles with tool name and size; "get" retrieves a pinned result by handle, optionally extracting a sub-field via path; "unpin" frees memory by removing a pinned result.`, "enum": []string{"list", "get", "unpin"}, }, "handle": map[string]any{ @@ -460,10 +419,14 @@ func (s *Server) buildSearchIndex() { // — the same shape vendor MCPs emit. Errors fall back to zero (i.e., no credit // claimed) rather than failing the whole index build. func computeCatalogBytes(tools []toolWithIntegration) int64 { + type wireProp struct { + Type string `json:"type"` + Description string `json:"description"` + } type schema struct { - Type string `json:"type"` - Properties map[string]string `json:"properties,omitempty"` - Required []string `json:"required,omitempty"` + Type string `json:"type"` + Properties map[string]wireProp `json:"properties,omitempty"` + Required []string `json:"required,omitempty"` } type listing struct { Name mcp.ToolName `json:"name"` @@ -472,13 +435,22 @@ func computeCatalogBytes(tools []toolWithIntegration) int64 { } entries := make([]listing, 0, len(tools)) for _, t := range tools { + props := make(map[string]wireProp, len(t.Tool.Parameters)) + var required []string + for _, p := range t.Tool.Parameters { + props[string(p.Name)] = wireProp{Type: "string", Description: p.Description} + if p.Required { + required = append(required, string(p.Name)) + } + } + sort.Strings(required) entries = append(entries, listing{ Name: t.Tool.Name, Description: t.Tool.Description, InputSchema: schema{ Type: "object", - Properties: t.Tool.Parameters, - Required: t.Tool.Required, + Properties: props, + Required: required, }, }) } @@ -583,10 +555,7 @@ func (s *Server) handleSearch(ctx context.Context, req *mcpsdk.CallToolRequest) Tools []toolInfo `json:"tools"` } - summary := fmt.Sprintf("Found %d tools", total) - if query != "" { - summary += fmt.Sprintf(" matching %q", args.Query) - } + summary := prompts.SearchSummary(prompts.Context{}, total, args.Query) var scriptHint string if total > 1 { @@ -595,9 +564,9 @@ func (s *Server) handleSearch(ctx context.Context, req *mcpsdk.CallToolRequest) seen[t.Integration] = true } if len(seen) >= 2 { - scriptHint = "These tools span multiple integrations. Use execute with a script to chain them in a single call — intermediate results stay server-side and never enter the conversation." + scriptHint = prompts.SearchHintMulti(prompts.Context{}) } else if total > 1 { - scriptHint = "Tip: if your task requires multiple tool calls, use execute with a script to chain them in a single call and reduce token usage." + scriptHint = prompts.SearchHintSingle(prompts.Context{}) } } @@ -692,23 +661,47 @@ func (s *Server) unrankedSearch(integration string, searchable []searchableInteg } // extractSharedParameters finds parameters with identical name+description -// across 3+ tools in the page, moves them to a shared map, and removes them -// from per-tool parameters. This deduplicates common params like "owner" and -// "repo" that appear verbatim across dozens of tools in an integration. +// AND identical Required flag across 3+ tools in the page, moves them to a +// shared map, and removes them from per-tool parameters. This deduplicates +// common params like "owner" and "repo" that appear verbatim across dozens +// of tools in an integration. +// +// Required is part of the dedup key because it is part of the parameter's +// semantic identity. A param with `required:true` in some tools and +// `required:false` in others is two distinct parameters that happen to share +// a name, and collapsing them would emit a misleading shared entry. When the +// two variants both meet the count threshold, the name is conflicted and not +// extracted; it stays per-tool. +// +// Required-ness of extracted shared params survives the wire boundary via +// searchToolInfo.Required, which is snapshotted at construction. This +// function only mutates Parameters; it does not touch Required. +// +// Each searchToolInfo.Parameters is a clone (via slices.Clone in toToolInfo / +// toolDefToInfo), so filtering in place here does not corrupt the original +// ToolDefinition stored in the search index. func extractSharedParameters(tools []searchToolInfo) map[string]string { const minCount = 3 - type paramKey struct{ name, desc string } + type paramKey struct { + name string + desc string + required bool + } counts := map[paramKey]int{} for _, t := range tools { - for name, desc := range t.Parameters { - counts[paramKey{name, desc}]++ + for _, p := range t.Parameters { + counts[paramKey{string(p.Name), p.Description, p.Required}]++ } } - // For each param name, collect all descriptions that meet the threshold. - // A name with multiple qualifying descriptions is ambiguous — skip it. - candidates := map[string]string{} // name → description + // For each param name, collect all (desc, required) pairs that meet the + // threshold. A name with multiple qualifying pairs is ambiguous — skip it. + type candidate struct { + desc string + required bool + } + candidates := map[string]candidate{} conflicted := map[string]bool{} for pk, count := range counts { if count < minCount { @@ -717,12 +710,13 @@ func extractSharedParameters(tools []searchToolInfo) map[string]string { if conflicted[pk.name] { continue } - if prev, exists := candidates[pk.name]; exists && prev != pk.desc { + c := candidate{desc: pk.desc, required: pk.required} + if prev, exists := candidates[pk.name]; exists && prev != c { delete(candidates, pk.name) conflicted[pk.name] = true continue } - candidates[pk.name] = pk.desc + candidates[pk.name] = c } if len(candidates) == 0 { @@ -730,14 +724,21 @@ func extractSharedParameters(tools []searchToolInfo) map[string]string { } for i := range tools { - for name, desc := range tools[i].Parameters { - if candidates[name] == desc { - delete(tools[i].Parameters, name) + filtered := tools[i].Parameters[:0] + for _, p := range tools[i].Parameters { + if c, ok := candidates[string(p.Name)]; ok && c == (candidate{desc: p.Description, required: p.Required}) { + continue } + filtered = append(filtered, p) } + tools[i].Parameters = filtered } - return candidates + shared := make(map[string]string, len(candidates)) + for name, c := range candidates { + shared[name] = c.desc + } + return shared } func (s *Server) handleExecute(ctx context.Context, req *mcpsdk.CallToolRequest) (*mcpsdk.CallToolResult, error) { @@ -1187,7 +1188,7 @@ func tooLargeEnvelope(toolName mcp.ToolName, size, limit int) string { "tool": string(toolName), "size": size, "limit": limit, - "hint": "narrow your query (e.g., add a filter, reduce page size, or request fewer fields)", + "hint": prompts.ResponseTooLargeHint(prompts.Context{}), } out, err := json.Marshal(envelope) if err != nil { @@ -1264,10 +1265,7 @@ func (s *Server) executeTool(ctx context.Context, toolName mcp.ToolName, args ma s.services.Metrics.RecordCircuitBreak(mcp.IntegrationName(integration.Name())) } return nil, &mcp.ToolResult{ - Data: fmt.Sprintf( - "integration %q temporarily unavailable (circuit breaker open, try again in ~%ds). Other integrations still work.", - integration.Name(), int(s.breakerCooldown.Seconds()), - ), + Data: prompts.CircuitBreaker(prompts.Context{}, integration.Name(), int(s.breakerCooldown.Seconds())), IsError: true, }, nil } @@ -1388,9 +1386,16 @@ func reservedArgsFor(integration mcp.Integration, toolName mcp.ToolName) []strin // compact.ReservedArgs() via reservedArgsFor so view/format reach // parseViewSelection instead of failing as unknown parameters. func validateArgs(tool mcp.ToolDefinition, args map[string]any, allowedExtras []string) error { - for _, req := range tool.Required { - if _, ok := args[req]; !ok { - return fmt.Errorf("missing required parameter %q for tool %q. Required: %v", req, tool.Name, tool.Required) + paramByName := make(map[string]mcp.Parameter, len(tool.Parameters)) + for _, p := range tool.Parameters { + paramByName[string(p.Name)] = p + } + for _, p := range tool.Parameters { + if !p.Required { + continue + } + if _, ok := args[string(p.Name)]; !ok { + return fmt.Errorf("missing required parameter %q for tool %q", string(p.Name), tool.Name) } } if len(tool.Parameters) == 0 { @@ -1401,7 +1406,7 @@ func validateArgs(tool mcp.ToolDefinition, args map[string]any, allowedExtras [] extras[k] = struct{}{} } for key := range args { - if _, ok := tool.Parameters[key]; ok { + if _, ok := paramByName[key]; ok { continue } if _, ok := extras[key]; ok { @@ -1425,10 +1430,11 @@ func unknownParamError(key string, tool mcp.ToolDefinition) error { // closestParam returns the parameter name closest to key by edit distance, // or empty string if no parameter is within a reasonable threshold. -func closestParam(key string, params map[string]string) string { +func closestParam(key string, params []mcp.Parameter) string { best := "" bestDist := len(key)/2 + 1 // threshold: half the key length - for name := range params { + for _, p := range params { + name := string(p.Name) d := editDistance(key, name) if d < bestDist { bestDist = d @@ -1467,10 +1473,10 @@ func editDistance(a, b string) int { } // paramNames returns sorted parameter names for error messages. -func paramNames(params map[string]string) []string { +func paramNames(params []mcp.Parameter) []string { names := make([]string, 0, len(params)) - for name := range params { - names = append(names, name) + for _, p := range params { + names = append(names, string(p.Name)) } slices.Sort(names) return names diff --git a/server/server_test.go b/server/server_test.go index 38f0cdf8..7a3f2c7d 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -217,7 +217,7 @@ func TestServerWithIntegration(t *testing.T) { { Name: mcp.ToolName("testint_list_items"), Description: "List test items", - Parameters: map[string]string{"query": "Search query"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("query"), Description: "Search query"}}, }, }, execFn: func(_ context.Context, toolName mcp.ToolName, args map[string]any) (*mcp.ToolResult, error) { @@ -410,6 +410,9 @@ func searchToolNames(t *testing.T, resp searchResponse) []string { } // extractColumnarParams parses the parameters column from columnar search tools JSON. +// Parameters are now []mcp.Parameter (array of {name,description,required}) — this +// helper converts them to map[string]string (name→description) for backward-compatible +// test assertions. func extractColumnarParams(t *testing.T, toolsRaw json.RawMessage) []map[string]string { t.Helper() var obj struct { @@ -429,9 +432,23 @@ func extractColumnarParams(t *testing.T, toolsRaw json.RawMessage) []map[string] var result []map[string]string for _, row := range obj.Rows { + // Parameters may be a JSON array ([{name,description}...]) or object. var params map[string]string - require.NoError(t, json.Unmarshal(row[paramIdx], ¶ms)) - result = append(result, params) + if err := json.Unmarshal(row[paramIdx], ¶ms); err == nil { + result = append(result, params) + continue + } + // Try parsing as []mcp.Parameter array. + var paramArr []struct { + Name string `json:"name"` + Description string `json:"description"` + } + require.NoError(t, json.Unmarshal(row[paramIdx], ¶mArr)) + m := make(map[string]string, len(paramArr)) + for _, p := range paramArr { + m[p.Name] = p.Description + } + result = append(result, m) } return result } @@ -508,7 +525,7 @@ func makeManyTools(prefix string, n int) []mcp.ToolDefinition { tools[i] = mcp.ToolDefinition{ Name: mcp.ToolName(fmt.Sprintf("%s_tool_%d", prefix, i)), Description: fmt.Sprintf("Tool %d for %s", i, prefix), - Parameters: map[string]string{"id": "the id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "the id"}}, } } return tools @@ -1560,8 +1577,7 @@ func TestHandleExecute_ViewArgsPassValidatorOnViewAwareTool(t *testing.T) { { Name: mcp.ToolName("vw_get"), Description: "Returns shapes", - Parameters: map[string]string{"id": "Record ID"}, - Required: []string{"id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "Record ID", Required: true}}, }, }, execFn: func(_ context.Context, _ mcp.ToolName, _ map[string]any) (*mcp.ToolResult, error) { @@ -1592,8 +1608,7 @@ func TestHandleExecute_ViewArgRejectedOnNonViewTool(t *testing.T) { { Name: mcp.ToolName("plain_get"), Description: "Returns shapes", - Parameters: map[string]string{"id": "Record ID"}, - Required: []string{"id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "Record ID", Required: true}}, }, }, } @@ -2670,13 +2685,12 @@ func TestFindTool_ReturnsToolDefinition(t *testing.T) { { Name: mcp.ToolName("testint_get_item"), Description: "Get an item", - Parameters: map[string]string{"id": "Item ID"}, - Required: []string{"id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "Item ID", Required: true}}, }, { Name: mcp.ToolName("testint_list_items"), Description: "List items", - Parameters: map[string]string{"query": "Search query"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("query"), Description: "Search query"}}, }, }, } @@ -2687,7 +2701,9 @@ func TestFindTool_ReturnsToolDefinition(t *testing.T) { require.NoError(t, err) assert.Equal(t, "testint", integration.Name()) assert.Equal(t, mcp.ToolName("testint_get_item"), toolDef.Name) - assert.Equal(t, []string{"id"}, toolDef.Required) + require.Len(t, toolDef.Parameters, 1) + assert.Equal(t, mcp.ParamName("id"), toolDef.Parameters[0].Name) + assert.True(t, toolDef.Parameters[0].Required) }) t.Run("returns error for unknown tool", func(t *testing.T) { @@ -2700,8 +2716,7 @@ func TestFindTool_ReturnsToolDefinition(t *testing.T) { func TestValidateArgs(t *testing.T) { tool := mcp.ToolDefinition{ Name: mcp.ToolName("github_get_issue"), - Parameters: map[string]string{"owner": "Repo owner", "repo": "Repo name", "number": "Issue number"}, - Required: []string{"owner", "repo", "number"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner", Required: true}, {Name: mcp.ParamName("repo"), Description: "Repo name", Required: true}, {Name: mcp.ParamName("number"), Description: "Issue number", Required: true}}, } tests := []struct { @@ -2744,8 +2759,7 @@ func TestValidateArgs(t *testing.T) { name: "optional param present", tool: mcp.ToolDefinition{ Name: mcp.ToolName("testint_list"), - Parameters: map[string]string{"query": "Search", "page": "Page number"}, - Required: []string{"query"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("query"), Description: "Search", Required: true}, {Name: mcp.ParamName("page"), Description: "Page number"}}, }, args: map[string]any{"query": "test", "page": 2}, }, @@ -2753,7 +2767,7 @@ func TestValidateArgs(t *testing.T) { name: "empty args with no required", tool: mcp.ToolDefinition{ Name: mcp.ToolName("testint_list"), - Parameters: map[string]string{"query": "Search"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("query"), Description: "Search"}}, }, args: map[string]any{}, }, @@ -2761,7 +2775,7 @@ func TestValidateArgs(t *testing.T) { name: "nil args with no required", tool: mcp.ToolDefinition{ Name: mcp.ToolName("testint_list"), - Parameters: map[string]string{"query": "Search"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("query"), Description: "Search"}}, }, args: nil, }, @@ -2814,8 +2828,7 @@ func TestExecuteTool_ValidationRejectsMissingRequired(t *testing.T) { { Name: mcp.ToolName("testint_get_item"), Description: "Get an item", - Parameters: map[string]string{"id": "Item ID", "format": "Output format"}, - Required: []string{"id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "Item ID", Required: true}, {Name: mcp.ParamName("format"), Description: "Output format"}}, }, }, execFn: func(_ context.Context, _ mcp.ToolName, _ map[string]any) (*mcp.ToolResult, error) { @@ -2840,8 +2853,7 @@ func TestExecuteTool_ValidationRejectsUnknownParam(t *testing.T) { { Name: mcp.ToolName("testint_get_item"), Description: "Get an item", - Parameters: map[string]string{"id": "Item ID"}, - Required: []string{"id"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "Item ID", Required: true}}, }, }, execFn: func(_ context.Context, _ mcp.ToolName, _ map[string]any) (*mcp.ToolResult, error) { @@ -2966,14 +2978,14 @@ func TestSearch_SharedParametersExtracted(t *testing.T) { { name: "extracts params with identical description across 3+ tools", tools: []mcp.ToolDefinition{ - {Name: mcp.ToolName("t_list_issues"), Description: "List issues", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "open or closed"}}, - {Name: mcp.ToolName("t_get_issue"), Description: "Get issue", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "issue_number": "Issue number"}}, - {Name: mcp.ToolName("t_list_pulls"), Description: "List pulls", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "base": "Base branch"}}, - {Name: mcp.ToolName("t_get_pull"), Description: "Get pull", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull number"}}, - {Name: mcp.ToolName("t_list_commits"), Description: "List commits", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Branch or SHA"}}, - {Name: mcp.ToolName("t_list_branches"), Description: "List branches", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, - {Name: mcp.ToolName("t_list_releases"), Description: "List releases", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, - {Name: mcp.ToolName("t_list_tags"), Description: "List tags", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, + {Name: mcp.ToolName("t_list_issues"), Description: "List issues", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("state"), Description: "open or closed"}}}, + {Name: mcp.ToolName("t_get_issue"), Description: "Get issue", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("issue_number"), Description: "Issue number"}}}, + {Name: mcp.ToolName("t_list_pulls"), Description: "List pulls", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("base"), Description: "Base branch"}}}, + {Name: mcp.ToolName("t_get_pull"), Description: "Get pull", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("pull_number"), Description: "Pull number"}}}, + {Name: mcp.ToolName("t_list_commits"), Description: "List commits", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("sha"), Description: "Branch or SHA"}}}, + {Name: mcp.ToolName("t_list_branches"), Description: "List branches", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, + {Name: mcp.ToolName("t_list_releases"), Description: "List releases", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, + {Name: mcp.ToolName("t_list_tags"), Description: "List tags", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, }, wantSharedParams: map[string]string{ "owner": "Repository owner", @@ -2984,14 +2996,14 @@ func TestSearch_SharedParametersExtracted(t *testing.T) { { name: "keeps params with same name but different description per-tool", tools: []mcp.ToolDefinition{ - {Name: mcp.ToolName("t_one"), Description: "One", Parameters: map[string]string{"id": "The issue ID"}}, - {Name: mcp.ToolName("t_two"), Description: "Two", Parameters: map[string]string{"id": "The pull request ID"}}, - {Name: mcp.ToolName("t_three"), Description: "Three", Parameters: map[string]string{"id": "The commit SHA"}}, - {Name: mcp.ToolName("t_four"), Description: "Four", Parameters: map[string]string{"id": "The release ID"}}, - {Name: mcp.ToolName("t_five"), Description: "Five", Parameters: map[string]string{"id": "The tag name"}}, - {Name: mcp.ToolName("t_six"), Description: "Six", Parameters: map[string]string{"id": "The branch name"}}, - {Name: mcp.ToolName("t_seven"), Description: "Seven", Parameters: map[string]string{"id": "The deploy ID"}}, - {Name: mcp.ToolName("t_eight"), Description: "Eight", Parameters: map[string]string{"id": "The run ID"}}, + {Name: mcp.ToolName("t_one"), Description: "One", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The issue ID"}}}, + {Name: mcp.ToolName("t_two"), Description: "Two", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The pull request ID"}}}, + {Name: mcp.ToolName("t_three"), Description: "Three", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The commit SHA"}}}, + {Name: mcp.ToolName("t_four"), Description: "Four", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The release ID"}}}, + {Name: mcp.ToolName("t_five"), Description: "Five", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The tag name"}}}, + {Name: mcp.ToolName("t_six"), Description: "Six", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The branch name"}}}, + {Name: mcp.ToolName("t_seven"), Description: "Seven", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The deploy ID"}}}, + {Name: mcp.ToolName("t_eight"), Description: "Eight", Parameters: []mcp.Parameter{{Name: mcp.ParamName("id"), Description: "The run ID"}}}, }, wantSharedParams: nil, // all different descriptions wantKeptPerTool: []string{"id"}, @@ -2999,14 +3011,14 @@ func TestSearch_SharedParametersExtracted(t *testing.T) { { name: "preserves tool-specific value hints even when name matches", tools: []mcp.ToolDefinition{ - {Name: mcp.ToolName("t_a"), Description: "A", Parameters: map[string]string{"event": "APPROVE, REQUEST_CHANGES, COMMENT", "owner": "Repo owner"}}, - {Name: mcp.ToolName("t_b"), Description: "B", Parameters: map[string]string{"event": "push, pull_request", "owner": "Repo owner"}}, - {Name: mcp.ToolName("t_c"), Description: "C", Parameters: map[string]string{"event": "issues, created", "owner": "Repo owner"}}, - {Name: mcp.ToolName("t_d"), Description: "D", Parameters: map[string]string{"owner": "Repo owner"}}, - {Name: mcp.ToolName("t_e"), Description: "E", Parameters: map[string]string{"event": "deployment", "owner": "Repo owner"}}, - {Name: mcp.ToolName("t_f"), Description: "F", Parameters: map[string]string{"event": "release", "owner": "Repo owner"}}, - {Name: mcp.ToolName("t_g"), Description: "G", Parameters: map[string]string{"owner": "Repo owner"}}, - {Name: mcp.ToolName("t_h"), Description: "H", Parameters: map[string]string{"owner": "Repo owner"}}, + {Name: mcp.ToolName("t_a"), Description: "A", Parameters: []mcp.Parameter{{Name: mcp.ParamName("event"), Description: "APPROVE, REQUEST_CHANGES, COMMENT"}, {Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_b"), Description: "B", Parameters: []mcp.Parameter{{Name: mcp.ParamName("event"), Description: "push, pull_request"}, {Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_c"), Description: "C", Parameters: []mcp.Parameter{{Name: mcp.ParamName("event"), Description: "issues, created"}, {Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_d"), Description: "D", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_e"), Description: "E", Parameters: []mcp.Parameter{{Name: mcp.ParamName("event"), Description: "deployment"}, {Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_f"), Description: "F", Parameters: []mcp.Parameter{{Name: mcp.ParamName("event"), Description: "release"}, {Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_g"), Description: "G", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, + {Name: mcp.ToolName("t_h"), Description: "H", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner"}}}, }, wantSharedParams: map[string]string{"owner": "Repo owner"}, wantKeptPerTool: []string{"event"}, // different descriptions → stays per-tool @@ -3073,28 +3085,37 @@ func TestSearch_SharedParametersDoNotMutateOriginalTools(t *testing.T) { name: "testint", healthy: true, tools: []mcp.ToolDefinition{ - {Name: mcp.ToolName("t_list_issues"), Description: "List issues", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "state": "open or closed"}}, - {Name: mcp.ToolName("t_get_issue"), Description: "Get issue", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "issue_number": "Issue number"}}, - {Name: mcp.ToolName("t_list_pulls"), Description: "List pulls", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "base": "Base branch"}}, - {Name: mcp.ToolName("t_get_pull"), Description: "Get pull", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "pull_number": "Pull number"}}, - {Name: mcp.ToolName("t_list_commits"), Description: "List commits", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name", "sha": "Branch or SHA"}}, - {Name: mcp.ToolName("t_list_branches"), Description: "List branches", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, - {Name: mcp.ToolName("t_list_releases"), Description: "List releases", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, - {Name: mcp.ToolName("t_list_tags"), Description: "List tags", Parameters: map[string]string{"owner": "Repository owner", "repo": "Repository name"}}, + {Name: mcp.ToolName("t_list_issues"), Description: "List issues", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("state"), Description: "open or closed"}}}, + {Name: mcp.ToolName("t_get_issue"), Description: "Get issue", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("issue_number"), Description: "Issue number"}}}, + {Name: mcp.ToolName("t_list_pulls"), Description: "List pulls", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("base"), Description: "Base branch"}}}, + {Name: mcp.ToolName("t_get_pull"), Description: "Get pull", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("pull_number"), Description: "Pull number"}}}, + {Name: mcp.ToolName("t_list_commits"), Description: "List commits", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}, {Name: mcp.ParamName("sha"), Description: "Branch or SHA"}}}, + {Name: mcp.ToolName("t_list_branches"), Description: "List branches", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, + {Name: mcp.ToolName("t_list_releases"), Description: "List releases", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, + {Name: mcp.ToolName("t_list_tags"), Description: "List tags", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}, {Name: mcp.ParamName("repo"), Description: "Repository name"}}}, }, } - s := setupTestServer(mi) // First search triggers shared parameter extraction — must not corrupt originals. + s := setupTestServer(mi) + _, err := s.handleSearch(context.Background(), searchRequest(map[string]any{})) require.NoError(t, err) - // Verify the original ToolDefinition maps are untouched. + // Verify the original ToolDefinition slices are untouched after search. for _, tool := range mi.tools { - assert.Contains(t, tool.Parameters, "owner", - "tool %q should still have 'owner' after search (was deleted from original)", tool.Name) - assert.Contains(t, tool.Parameters, "repo", - "tool %q should still have 'repo' after search (was deleted from original)", tool.Name) + hasOwner := false + hasRepo := false + for _, p := range tool.Parameters { + if string(p.Name) == "owner" { + hasOwner = true + } + if string(p.Name) == "repo" { + hasRepo = true + } + } + assert.True(t, hasOwner, "tool %q should still have 'owner' after search (was deleted from original)", tool.Name) + assert.True(t, hasRepo, "tool %q should still have 'repo' after search (was deleted from original)", tool.Name) } // Second search should produce identical shared_parameters as the first. @@ -3114,6 +3135,159 @@ func TestSearch_SharedParametersDoNotMutateOriginalTools(t *testing.T) { }, shared, "second search should produce the same shared_parameters") } +// TestSearch_DisagreeingRequiredStaysPerTool verifies that when the same +// parameter name+description appears with disagreeing `required` flags across +// the page (both variants meet the dedup threshold), the parameter is +// considered conflicted and is NOT extracted to shared_parameters. It stays +// per-tool with each tool's correct required flag intact. +// +// Required is part of the parameter's semantic identity: `owner: required:true` +// in one tool and `owner: required:false` in another are distinct parameters +// that happen to share a name. Pre-Phase-0 the wire kept `required[]` separate +// from the parameter map and survived extraction by construction; post-reshape +// it is derived from the per-tool Parameter slice, so a shared param's +// required-ness must be preserved by keeping the parameter per-tool when it +// would otherwise be lost. +func TestSearch_DisagreeingRequiredStaysPerTool(t *testing.T) { + mi := &mockIntegration{ + name: "testint", + healthy: true, + tools: []mcp.ToolDefinition{ + // 3 tools with owner Required:true and 3 with Required:false — both + // variants individually meet the count>=3 threshold, so both qualify. + // The conflict resolver must catch this and leave owner per-tool. + {Name: mcp.ToolName("t_one"), Description: "One", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}}}, + {Name: mcp.ToolName("t_two"), Description: "Two", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}}}, + {Name: mcp.ToolName("t_three"), Description: "Three", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}}}, + {Name: mcp.ToolName("t_four"), Description: "Four", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}}}, + {Name: mcp.ToolName("t_five"), Description: "Five", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}}}, + {Name: mcp.ToolName("t_six"), Description: "Six", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner"}}}, + }, + } + s := setupTestServer(mi) + + result, err := s.handleSearch(context.Background(), searchRequest(map[string]any{})) + require.NoError(t, err) + require.False(t, result.IsError) + + tc, ok := result.Content[0].(*mcpsdk.TextContent) + require.True(t, ok) + + var raw map[string]json.RawMessage + require.NoError(t, json.Unmarshal([]byte(tc.Text), &raw)) + + // owner has disagreeing required flags across tools → must not be extracted. + if sharedRaw, hasShared := raw["shared_parameters"]; hasShared { + var shared map[string]string + require.NoError(t, json.Unmarshal(sharedRaw, &shared)) + assert.NotContains(t, shared, "owner", + "owner has disagreeing required flags — must stay per-tool, not collapse to shared") + } + + // Every tool should keep `owner` per-tool, and the required[] array should + // reflect each tool's own flag. + var toolsWire []struct { + Name string `json:"name"` + Parameters map[string]string `json:"parameters"` + Required []string `json:"required"` + } + require.NoError(t, json.Unmarshal(raw["tools"], &toolsWire)) + + requiredByTool := map[string][]string{} + paramsByTool := map[string]map[string]string{} + for _, tw := range toolsWire { + requiredByTool[tw.Name] = tw.Required + paramsByTool[tw.Name] = tw.Parameters + } + for _, name := range []string{"t_one", "t_two", "t_three", "t_four", "t_five", "t_six"} { + assert.Contains(t, paramsByTool[name], "owner", + "%s should keep owner per-tool when required-ness disagrees", name) + } + for _, name := range []string{"t_one", "t_two", "t_three"} { + assert.Contains(t, requiredByTool[name], "owner", + "%s had owner Required:true — required[] must include owner", name) + } + for _, name := range []string{"t_four", "t_five", "t_six"} { + assert.NotContains(t, requiredByTool[name], "owner", + "%s had owner Required:false — required[] must not include owner", name) + } +} + +// TestSearch_SharedRequiredPreservedAtWire is the parse-don't-validate +// regression gate for the search response's `required[]` array. +// +// When `owner` is Required:true across many tools and shares description, it +// gets extracted to shared_parameters — its entry leaves each tool's +// Parameters slice. Pre-Phase-0 the wire's required[] was a separate field +// that survived extraction by construction. Post-reshape the field exists as +// a snapshot taken at searchToolInfo construction (see toToolInfo / +// toolDefToInfo). MarshalJSON emits it directly; it is NOT re-derived from +// the post-extraction Parameters slice. This test pins that contract. +// +// The failure mode this prevents: shared+required params silently lose the +// required signal in the wire response, so the LLM treats them as optional. +func TestSearch_SharedRequiredPreservedAtWire(t *testing.T) { + mi := &mockIntegration{ + name: "testint", + healthy: true, + tools: []mcp.ToolDefinition{ + {Name: mcp.ToolName("t_one"), Description: "One", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}, {Name: mcp.ParamName("issue_number"), Description: "Issue ID", Required: true}}}, + {Name: mcp.ToolName("t_two"), Description: "Two", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}, {Name: mcp.ParamName("pr_number"), Description: "Pull number", Required: true}}}, + {Name: mcp.ToolName("t_three"), Description: "Three", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}, {Name: mcp.ParamName("sha"), Description: "Commit SHA", Required: true}}}, + {Name: mcp.ToolName("t_four"), Description: "Four", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repository owner", Required: true}}}, + }, + } + s := setupTestServer(mi) + + result, err := s.handleSearch(context.Background(), searchRequest(map[string]any{})) + require.NoError(t, err) + require.False(t, result.IsError) + + tc, ok := result.Content[0].(*mcpsdk.TextContent) + require.True(t, ok) + + var raw map[string]json.RawMessage + require.NoError(t, json.Unmarshal([]byte(tc.Text), &raw)) + + // owner is uniformly required across all 4 tools → extracted to shared. + var shared map[string]string + require.NoError(t, json.Unmarshal(raw["shared_parameters"], &shared)) + assert.Equal(t, map[string]string{"owner": "Repository owner"}, shared, + "owner should be extracted to shared since name+desc+required match") + + // And owner is removed from each tool's parameters. + var toolsWire []struct { + Name string `json:"name"` + Parameters map[string]string `json:"parameters"` + Required []string `json:"required"` + } + require.NoError(t, json.Unmarshal(raw["tools"], &toolsWire)) + + requiredByTool := map[string][]string{} + paramsByTool := map[string]map[string]string{} + for _, tw := range toolsWire { + requiredByTool[tw.Name] = tw.Required + paramsByTool[tw.Name] = tw.Parameters + } + for _, name := range []string{"t_one", "t_two", "t_three", "t_four"} { + assert.NotContains(t, paramsByTool[name], "owner", + "%s should have owner removed from params (it's in shared)", name) + } + + // THIS IS THE pdv ASSERTION: required[] must still name `owner` for every + // tool. The snapshot at construction carried the proof; extraction did not + // touch it. Without the snapshot, required[] would be derived from the + // post-extraction Parameters and the signal would be lost. + for _, name := range []string{"t_one", "t_two", "t_three", "t_four"} { + assert.Contains(t, requiredByTool[name], "owner", + "%s.required[] must still name owner — snapshot proves required-ness independent of shared extraction", name) + } + // And tool-specific required params survive extraction too. + assert.Contains(t, requiredByTool["t_one"], "issue_number") + assert.Contains(t, requiredByTool["t_two"], "pr_number") + assert.Contains(t, requiredByTool["t_three"], "sha") +} + // --- ABAC tool glob filtering tests --- func setupTestServerWithGlobs(integrations map[string]*mockIntegration, globs map[string][]string) *Server { diff --git a/server/session_handlers_test.go b/server/session_handlers_test.go index 0b9d82ad..a6802e2b 100644 --- a/server/session_handlers_test.go +++ b/server/session_handlers_test.go @@ -113,8 +113,7 @@ func TestHandleExecute_SessionContextInjected(t *testing.T) { { Name: "github_list_issues", Description: "List issues", - Parameters: map[string]string{"owner": "Repo owner", "repo": "Repo name", "state": "Issue state"}, - Required: []string{"owner", "repo"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner", Required: true}, {Name: mcp.ParamName("repo"), Description: "Repo name", Required: true}, {Name: mcp.ParamName("state"), Description: "Issue state"}}, }, }, execFn: func(_ context.Context, _ mcp.ToolName, args map[string]any) (*mcp.ToolResult, error) { @@ -146,8 +145,7 @@ func TestHandleExecute_ExplicitArgsOverrideSession(t *testing.T) { { Name: "github_list_issues", Description: "List issues", - Parameters: map[string]string{"owner": "Repo owner", "repo": "Repo name"}, - Required: []string{"owner", "repo"}, + Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner", Required: true}, {Name: mcp.ParamName("repo"), Description: "Repo name", Required: true}}, }, }, execFn: func(_ context.Context, _ mcp.ToolName, args map[string]any) (*mcp.ToolResult, error) { diff --git a/server/session_pin_handlers_test.go b/server/session_pin_handlers_test.go index 2a6bdb2b..a7fdf8b0 100644 --- a/server/session_pin_handlers_test.go +++ b/server/session_pin_handlers_test.go @@ -55,10 +55,7 @@ func TestHandleExecute_RefResolution(t *testing.T) { healthy: true, tools: []mcp.ToolDefinition{ {Name: "github_get_repo", Description: "Get repo"}, - {Name: "github_get_issue", Description: "Get issue", Parameters: map[string]string{ - "owner": "Repo owner", - "issue_number": "Issue number", - }}, + {Name: "github_get_issue", Description: "Get issue", Parameters: []mcp.Parameter{{Name: mcp.ParamName("owner"), Description: "Repo owner"}, {Name: mcp.ParamName("issue_number"), Description: "Issue number"}}}, }, execFn: func(_ context.Context, tn mcp.ToolName, args map[string]any) (*mcp.ToolResult, error) { if tn == "github_get_repo" { diff --git a/server/tools_list.lock.json b/server/tools_list.lock.json new file mode 100644 index 00000000..f4d5585c --- /dev/null +++ b/server/tools_list.lock.json @@ -0,0 +1,38039 @@ +{ + "tools": [ + { + "name": "github_search_repos", + "description": "Search GitHub repositories. Start here to find repos by name, topic, or language.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_get_repo", + "description": "Get a repository by owner/name. Use after search_repos or when you already know the owner/repo.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_user_repos", + "description": "List repositories for a user. Use when you know the username; prefer search_repos for keyword discovery.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated, pushed, full_name" + }, + "type": { + "type": "string", + "description": "Type: all, owner, member (default: owner)" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "github_list_org_repos", + "description": "List repositories for an organization. Use when you know the org; prefer search_repos for keyword discovery.", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated, pushed, full_name" + }, + "type": { + "type": "string", + "description": "Type: all, public, private, forks, sources, member" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_create_repo", + "description": "Create a repository for the authenticated user or an org", + "inputSchema": { + "type": "object", + "properties": { + "auto_init": { + "type": "string", + "description": "Initialize with README (true/false)" + }, + "description": { + "type": "string", + "description": "Description" + }, + "name": { + "type": "string", + "description": "Repository name" + }, + "org": { + "type": "string", + "description": "Organization (omit for user repo)" + }, + "private": { + "type": "string", + "description": "Private repo (true/false)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "github_delete_repo", + "description": "Delete a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_branches", + "description": "List branches of a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_branch", + "description": "Get a specific branch", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Branch name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_tags", + "description": "List tags of a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_contributors", + "description": "List contributors to a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_languages", + "description": "List languages used in a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_topics", + "description": "List repository topics", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_readme", + "description": "Get the README for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Git ref (branch/tag/sha)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_file_contents", + "description": "Get file or directory contents from a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "path": { + "type": "string", + "description": "File path" + }, + "ref": { + "type": "string", + "description": "Git ref (branch/tag/sha)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "path", + "repo" + ] + } + }, + { + "name": "github_create_update_file", + "description": "Create or update a file in a repository", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Target branch" + }, + "content": { + "type": "string", + "description": "Raw file content (plain text, not base64 — encoding is handled automatically)" + }, + "message": { + "type": "string", + "description": "Commit message" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "path": { + "type": "string", + "description": "File path" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "SHA of file being replaced (required for update)" + } + }, + "required": [ + "content", + "message", + "owner", + "path", + "repo" + ] + } + }, + { + "name": "github_delete_file", + "description": "Delete a file from a repository", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Target branch" + }, + "message": { + "type": "string", + "description": "Commit message" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "path": { + "type": "string", + "description": "File path" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "SHA of file to delete" + } + }, + "required": [ + "message", + "owner", + "path", + "repo", + "sha" + ] + } + }, + { + "name": "github_list_forks", + "description": "List forks of a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sort": { + "type": "string", + "description": "Sort: newest, oldest, stargazers, watchers" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_fork", + "description": "Fork a repository", + "inputSchema": { + "type": "object", + "properties": { + "organization": { + "type": "string", + "description": "Organization to fork into" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_collaborators", + "description": "List collaborators on a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_commit_activity", + "description": "Get the last year of commit activity (weekly)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_repo_teams", + "description": "List teams with access to a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_compare_commits", + "description": "Compare two commits, branches, or tags. Use to see what changed between refs (commit list and diff). Start here for 'what changed in prod' queries.", + "inputSchema": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Base ref (branch, tag, or SHA)" + }, + "head": { + "type": "string", + "description": "Head ref (branch, tag, or SHA)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "base", + "head", + "owner", + "repo" + ] + } + }, + { + "name": "github_merge_upstream", + "description": "Sync a fork branch with the upstream repository", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Branch to sync" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_autolinks", + "description": "List autolink references for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_edit_repo", + "description": "Update a repository's settings (description, visibility, default branch, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Archived (true/false)" + }, + "default_branch": { + "type": "string", + "description": "Default branch name" + }, + "description": { + "type": "string", + "description": "New description" + }, + "has_issues": { + "type": "string", + "description": "Enable issues (true/false)" + }, + "has_projects": { + "type": "string", + "description": "Enable projects (true/false)" + }, + "has_wiki": { + "type": "string", + "description": "Enable wiki (true/false)" + }, + "homepage": { + "type": "string", + "description": "Homepage URL" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "private": { + "type": "string", + "description": "Private (true/false)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_replace_topics", + "description": "Replace all topics on a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "topics": { + "type": "string", + "description": "Comma-separated topic names" + } + }, + "required": [ + "owner", + "repo", + "topics" + ] + } + }, + { + "name": "github_rename_branch", + "description": "Rename a branch", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Current branch name" + }, + "new_name": { + "type": "string", + "description": "New branch name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "new_name", + "owner", + "repo" + ] + } + }, + { + "name": "github_add_collaborator", + "description": "Add a collaborator to a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "permission": { + "type": "string", + "description": "Permission: pull, triage, push, maintain, admin" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "username": { + "type": "string", + "description": "User to add" + } + }, + "required": [ + "owner", + "repo", + "username" + ] + } + }, + { + "name": "github_remove_collaborator", + "description": "Remove a collaborator from a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "username": { + "type": "string", + "description": "User to remove" + } + }, + "required": [ + "owner", + "repo", + "username" + ] + } + }, + { + "name": "github_get_combined_status", + "description": "Get the combined commit status for a ref (aggregates all status checks)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref (SHA, branch, or tag)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_list_statuses", + "description": "List commit statuses for a ref", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref (SHA, branch, or tag)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_create_status", + "description": "Create a commit status", + "inputSchema": { + "type": "object", + "properties": { + "context": { + "type": "string", + "description": "Status context identifier" + }, + "description": { + "type": "string", + "description": "Short description" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Commit SHA" + }, + "state": { + "type": "string", + "description": "State: error, failure, pending, success" + }, + "target_url": { + "type": "string", + "description": "URL to associate with status" + } + }, + "required": [ + "owner", + "repo", + "sha", + "state" + ] + } + }, + { + "name": "github_list_deployments", + "description": "List deployments for a repository. Start here for deploy status, recent deploys, and rollout history.", + "inputSchema": { + "type": "object", + "properties": { + "environment": { + "type": "string", + "description": "Filter by environment" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Filter by ref" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "task": { + "type": "string", + "description": "Filter by task" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_deployment", + "description": "Get a single deployment", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "deployment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_deployment", + "description": "Create a deployment", + "inputSchema": { + "type": "object", + "properties": { + "auto_merge": { + "type": "string", + "description": "Auto-merge default branch into ref (true/false)" + }, + "description": { + "type": "string", + "description": "Description" + }, + "environment": { + "type": "string", + "description": "Environment name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Ref to deploy (branch, tag, or SHA)" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "task": { + "type": "string", + "description": "Task (default: deploy)" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_list_deployment_statuses", + "description": "List statuses for a deployment. Use after list_deployments to check deploy progress or failure.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "deployment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_deployment_status", + "description": "Create a deployment status", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + }, + "description": { + "type": "string", + "description": "Description" + }, + "environment": { + "type": "string", + "description": "Override environment name" + }, + "log_url": { + "type": "string", + "description": "Log URL" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: error, failure, inactive, in_progress, queued, pending, success" + } + }, + "required": [ + "deployment_id", + "owner", + "repo", + "state" + ] + } + }, + { + "name": "github_list_environments", + "description": "List environments for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_environment", + "description": "Get a single environment", + "inputSchema": { + "type": "object", + "properties": { + "environment": { + "type": "string", + "description": "Environment name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "environment", + "owner", + "repo" + ] + } + }, + { + "name": "github_get_branch_protection", + "description": "Get branch protection rules", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Branch name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "owner", + "repo" + ] + } + }, + { + "name": "github_remove_branch_protection", + "description": "Remove branch protection rules", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Branch name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_rulesets", + "description": "List repository rulesets", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_ruleset", + "description": "Get a repository ruleset by ID", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "ruleset_id": { + "type": "string", + "description": "Ruleset ID" + } + }, + "required": [ + "owner", + "repo", + "ruleset_id" + ] + } + }, + { + "name": "github_get_rules_for_branch", + "description": "Get active rules that apply to a branch", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Branch name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "branch", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_traffic_views", + "description": "Get repository traffic page views (last 14 days, push access required)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "per": { + "type": "string", + "description": "Aggregation period: day, week" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_traffic_clones", + "description": "Get repository traffic clones (last 14 days, push access required)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "per": { + "type": "string", + "description": "Aggregation period: day, week" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_traffic_referrers", + "description": "Get top referral sources for a repository (push access required)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_traffic_paths", + "description": "Get popular content paths for a repository (push access required)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_community_health", + "description": "Get community health metrics for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_dispatch_event", + "description": "Trigger a repository dispatch event", + "inputSchema": { + "type": "object", + "properties": { + "event_type": { + "type": "string", + "description": "Custom event type string" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "event_type", + "owner", + "repo" + ] + } + }, + { + "name": "github_merge_branch", + "description": "Merge a branch into another (not a PR merge — use github_merge_pull for PRs)", + "inputSchema": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Branch to merge into" + }, + "commit_message": { + "type": "string", + "description": "Merge commit message" + }, + "head": { + "type": "string", + "description": "Branch to merge from" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "base", + "head", + "owner", + "repo" + ] + } + }, + { + "name": "github_edit_release", + "description": "Update a release", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "New release notes" + }, + "draft": { + "type": "string", + "description": "Draft (true/false)" + }, + "name": { + "type": "string", + "description": "New release name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "prerelease": { + "type": "string", + "description": "Pre-release (true/false)" + }, + "release_id": { + "type": "string", + "description": "Release ID" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "tag_name": { + "type": "string", + "description": "New tag name" + } + }, + "required": [ + "owner", + "release_id", + "repo" + ] + } + }, + { + "name": "github_generate_release_notes", + "description": "Auto-generate release notes content between two tags", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "previous_tag_name": { + "type": "string", + "description": "Previous tag to compare against" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "tag_name": { + "type": "string", + "description": "Tag for the release" + }, + "target_commitish": { + "type": "string", + "description": "Branch or SHA to tag" + } + }, + "required": [ + "owner", + "repo", + "tag_name" + ] + } + }, + { + "name": "github_list_commit_comments", + "description": "List comments on a specific commit", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Commit SHA" + } + }, + "required": [ + "owner", + "repo", + "sha" + ] + } + }, + { + "name": "github_create_commit_comment", + "description": "Create a comment on a commit", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Comment body" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "path": { + "type": "string", + "description": "Relative file path" + }, + "position": { + "type": "string", + "description": "Line position in the diff" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Commit SHA" + } + }, + "required": [ + "body", + "owner", + "repo", + "sha" + ] + } + }, + { + "name": "github_list_issues", + "description": "List issues for a repository. Start here for issue workflows when you know the repo. For cross-repo search, use search_issues.", + "inputSchema": { + "type": "object", + "properties": { + "assignee": { + "type": "string", + "description": "Filter by assignee username" + }, + "direction": { + "type": "string", + "description": "Direction: asc, desc" + }, + "labels": { + "type": "string", + "description": "Comma-separated label names" + }, + "milestone": { + "type": "string", + "description": "Milestone number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated, comments" + }, + "state": { + "type": "string", + "description": "State: open, closed, all" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_issue", + "description": "Get a single issue with full details. Use after list_issues or search_issues to drill into a specific issue.", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_issue", + "description": "Create an issue. Requires owner, repo, and title at minimum.", + "inputSchema": { + "type": "object", + "properties": { + "assignees": { + "type": "string", + "description": "Comma-separated assignee usernames" + }, + "body": { + "type": "string", + "description": "Issue body (markdown)" + }, + "labels": { + "type": "string", + "description": "Comma-separated label names" + }, + "milestone": { + "type": "string", + "description": "Milestone number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "title": { + "type": "string", + "description": "Issue title" + } + }, + "required": [ + "owner", + "repo", + "title" + ] + } + }, + { + "name": "github_update_issue", + "description": "Update an issue", + "inputSchema": { + "type": "object", + "properties": { + "assignees": { + "type": "string", + "description": "Comma-separated assignee usernames" + }, + "body": { + "type": "string", + "description": "New body" + }, + "labels": { + "type": "string", + "description": "Comma-separated label names" + }, + "milestone": { + "type": "string", + "description": "Milestone number" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: open, closed" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_issue_comments", + "description": "List comments on an issue", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_issue_comment", + "description": "Create a comment on an issue", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Comment body (markdown)" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "body", + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_issue_labels", + "description": "List labels on an issue", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_add_issue_labels", + "description": "Add labels to an issue", + "inputSchema": { + "type": "object", + "properties": { + "labels": { + "type": "string", + "description": "Comma-separated label names to add" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "labels", + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_remove_issue_label", + "description": "Remove a label from an issue", + "inputSchema": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Label name to remove" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "label", + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_lock_issue", + "description": "Lock an issue conversation", + "inputSchema": { + "type": "object", + "properties": { + "lock_reason": { + "type": "string", + "description": "Reason: off-topic, too heated, resolved, spam" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_unlock_issue", + "description": "Unlock an issue conversation", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_milestones", + "description": "List milestones for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sort": { + "type": "string", + "description": "Sort: due_on, completeness" + }, + "state": { + "type": "string", + "description": "State: open, closed, all" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_milestone", + "description": "Create a milestone", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description" + }, + "due_on": { + "type": "string", + "description": "Due date (ISO 8601 YYYY-MM-DDT00:00:00Z)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: open, closed" + }, + "title": { + "type": "string", + "description": "Milestone title" + } + }, + "required": [ + "owner", + "repo", + "title" + ] + } + }, + { + "name": "github_list_issue_events", + "description": "List events on an issue", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_issue_timeline", + "description": "List timeline events for an issue", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_assignees", + "description": "List available assignees for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_update_issue_comment", + "description": "Edit an issue or PR comment", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "New comment body (markdown)" + }, + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "body", + "comment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_delete_issue_comment", + "description": "Delete an issue or PR comment", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "comment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_update_milestone", + "description": "Update a milestone", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "due_on": { + "type": "string", + "description": "Due date (ISO 8601)" + }, + "number": { + "type": "string", + "description": "Milestone number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: open, closed" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_delete_milestone", + "description": "Delete a milestone", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Milestone number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_labels", + "description": "List all labels in a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_label", + "description": "Create a label in a repository", + "inputSchema": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Color hex code (without #)" + }, + "description": { + "type": "string", + "description": "Label description" + }, + "name": { + "type": "string", + "description": "Label name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "color", + "name", + "owner", + "repo" + ] + } + }, + { + "name": "github_edit_label", + "description": "Update a label", + "inputSchema": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "New color hex code (without #)" + }, + "description": { + "type": "string", + "description": "New description" + }, + "name": { + "type": "string", + "description": "Current label name" + }, + "new_name": { + "type": "string", + "description": "New label name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "name", + "owner", + "repo" + ] + } + }, + { + "name": "github_delete_label", + "description": "Delete a label from a repository", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Label name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "name", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_issue_reaction", + "description": "Add a reaction to an issue (+1, -1, laugh, confused, heart, hooray, rocket, eyes)", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes" + }, + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "content", + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_create_issue_comment_reaction", + "description": "Add a reaction to an issue comment", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "content": { + "type": "string", + "description": "Reaction: +1, -1, laugh, confused, heart, hooray, rocket, eyes" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "comment_id", + "content", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_issue_reactions", + "description": "List reactions on an issue", + "inputSchema": { + "type": "object", + "properties": { + "number": { + "type": "string", + "description": "Issue number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_pulls", + "description": "List pull requests for a repository. Start here for PR workflows when you know the repo. For cross-repo search, use search_issues with type:pr.", + "inputSchema": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Filter by base branch" + }, + "direction": { + "type": "string", + "description": "Direction: asc, desc" + }, + "head": { + "type": "string", + "description": "Filter by head user:branch" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated, popularity, long-running" + }, + "state": { + "type": "string", + "description": "State: open, closed, all" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_pull", + "description": "Get a single pull request with full details. Use after list_pulls to drill into a specific PR. For the diff, follow up with get_pull_diff.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_get_pull_diff", + "description": "Get the raw unified diff of a pull request. Use after get_pull for the full code diff.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_create_pull", + "description": "Create a pull request. Requires head branch and base branch.", + "inputSchema": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Base branch" + }, + "body": { + "type": "string", + "description": "PR body (markdown)" + }, + "draft": { + "type": "string", + "description": "Create as draft (true/false)" + }, + "head": { + "type": "string", + "description": "Head branch (or user:branch for cross-repo)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "title": { + "type": "string", + "description": "PR title" + } + }, + "required": [ + "base", + "head", + "owner", + "repo", + "title" + ] + } + }, + { + "name": "github_update_pull", + "description": "Update a pull request", + "inputSchema": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "New base branch" + }, + "body": { + "type": "string", + "description": "New body" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: open, closed" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_pull_commits", + "description": "List commits on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_pull_files", + "description": "List files changed in a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_pull_reviews", + "description": "List reviews on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_create_pull_review", + "description": "Create a review on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Review body" + }, + "event": { + "type": "string", + "description": "Review action: APPROVE, REQUEST_CHANGES, COMMENT" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "event", + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_pull_comments", + "description": "List review comments on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_create_pull_comment", + "description": "Create a review comment on a pull request diff", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Comment body" + }, + "commit_id": { + "type": "string", + "description": "SHA of the commit to comment on" + }, + "line": { + "type": "string", + "description": "Line number in the diff" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "path": { + "type": "string", + "description": "Relative file path" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "body", + "commit_id", + "owner", + "path", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_get_pull_comment", + "description": "Get a single review comment on a pull request by its comment ID", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "comment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_reply_to_pull_comment", + "description": "Reply to an existing review comment thread on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Reply body" + }, + "comment_id": { + "type": "string", + "description": "ID of the comment to reply to" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "body", + "comment_id", + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_update_pull_comment", + "description": "Update the body of a review comment on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "New comment body (markdown)" + }, + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "body", + "comment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_delete_pull_comment", + "description": "Delete a review comment on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "comment_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_merge_pull", + "description": "Merge a pull request", + "inputSchema": { + "type": "object", + "properties": { + "commit_message": { + "type": "string", + "description": "Merge commit message" + }, + "merge_method": { + "type": "string", + "description": "Method: merge, squash, rebase" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_requested_reviewers", + "description": "List requested reviewers on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_request_reviewers", + "description": "Request reviewers on a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "reviewers": { + "type": "string", + "description": "Comma-separated usernames" + }, + "team_reviewers": { + "type": "string", + "description": "Comma-separated team slugs" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_dismiss_pull_review", + "description": "Dismiss a pull request review", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Dismissal message" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "review_id": { + "type": "string", + "description": "Review ID" + } + }, + "required": [ + "message", + "owner", + "pull_number", + "repo", + "review_id" + ] + } + }, + { + "name": "github_update_pull_branch", + "description": "Update a PR branch with the latest changes from the base branch", + "inputSchema": { + "type": "object", + "properties": { + "expected_head_sha": { + "type": "string", + "description": "Expected SHA of the PR head (for optimistic locking)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_remove_reviewers", + "description": "Remove requested reviewers from a pull request", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "pull_number": { + "type": "string", + "description": "Pull request number" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "reviewers": { + "type": "string", + "description": "Comma-separated usernames" + }, + "team_reviewers": { + "type": "string", + "description": "Comma-separated team slugs" + } + }, + "required": [ + "owner", + "pull_number", + "repo" + ] + } + }, + { + "name": "github_list_pulls_with_commit", + "description": "List pull requests that contain a specific commit", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Commit SHA" + } + }, + "required": [ + "owner", + "repo", + "sha" + ] + } + }, + { + "name": "github_get_commit", + "description": "Get a commit by SHA including files changed. For comparing two refs, use compare_commits instead.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Commit SHA" + } + }, + "required": [ + "owner", + "repo", + "sha" + ] + } + }, + { + "name": "github_list_commits", + "description": "List commits on a branch", + "inputSchema": { + "type": "object", + "properties": { + "author": { + "type": "string", + "description": "GitHub login or email to filter by" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "path": { + "type": "string", + "description": "Only commits containing this file path" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Branch name or SHA to start listing from" + }, + "since": { + "type": "string", + "description": "Only commits after this date (ISO 8601)" + }, + "until": { + "type": "string", + "description": "Only commits before this date (ISO 8601)" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_ref", + "description": "Get a git reference (branch or tag)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Reference (e.g., heads/main, tags/v1.0)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_create_ref", + "description": "Create a git reference (branch or tag)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Full reference path (e.g., refs/heads/new-branch)" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "SHA to point the ref at" + } + }, + "required": [ + "owner", + "ref", + "repo", + "sha" + ] + } + }, + { + "name": "github_delete_ref", + "description": "Delete a git reference", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Reference to delete (e.g., heads/old-branch)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_get_tree", + "description": "Get a git tree (directory listing)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "recursive": { + "type": "string", + "description": "Recurse into subtrees (true/false)" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "Tree SHA or branch name" + } + }, + "required": [ + "owner", + "repo", + "sha" + ] + } + }, + { + "name": "github_create_tag", + "description": "Create an annotated tag object", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Tag message" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "sha": { + "type": "string", + "description": "SHA of object to tag" + }, + "tag": { + "type": "string", + "description": "Tag name" + }, + "type": { + "type": "string", + "description": "Object type: commit, tree, blob" + } + }, + "required": [ + "message", + "owner", + "repo", + "sha", + "tag" + ] + } + }, + { + "name": "github_get_authenticated_user", + "description": "Get the currently authenticated user", + "inputSchema": { + "type": "object" + } + }, + { + "name": "github_get_user", + "description": "Get a user by username", + "inputSchema": { + "type": "object", + "properties": { + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "github_list_user_followers", + "description": "List followers of a user", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "github_list_user_following", + "description": "List users that a user follows", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "github_list_user_keys", + "description": "List public SSH keys for a user", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "github_get_org", + "description": "Get an organization", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization login name" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_user_orgs", + "description": "List organizations for a user", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "username": { + "type": "string", + "description": "GitHub username (empty for authenticated user)" + } + } + } + }, + { + "name": "github_list_org_members", + "description": "List organization members", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "role": { + "type": "string", + "description": "Filter: all, admin, member" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_org_teams", + "description": "List teams in an organization", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_get_team_by_slug", + "description": "Get a team by slug", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "slug" + ] + } + }, + { + "name": "github_list_team_members", + "description": "List members of a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "role": { + "type": "string", + "description": "Filter: all, member, maintainer" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "slug" + ] + } + }, + { + "name": "github_list_team_repos", + "description": "List repositories for a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "slug" + ] + } + }, + { + "name": "github_create_team", + "description": "Create a team in an organization", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Team description" + }, + "name": { + "type": "string", + "description": "Team name" + }, + "org": { + "type": "string", + "description": "Organization name" + }, + "permission": { + "type": "string", + "description": "Default permission: pull, push" + }, + "privacy": { + "type": "string", + "description": "Privacy: secret, closed" + } + }, + "required": [ + "name", + "org" + ] + } + }, + { + "name": "github_edit_team", + "description": "Update a team", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "name": { + "type": "string", + "description": "New team name" + }, + "org": { + "type": "string", + "description": "Organization name" + }, + "permission": { + "type": "string", + "description": "Default permission: pull, push" + }, + "privacy": { + "type": "string", + "description": "Privacy: secret, closed" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "name", + "org", + "slug" + ] + } + }, + { + "name": "github_delete_team", + "description": "Delete a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "slug" + ] + } + }, + { + "name": "github_add_team_member", + "description": "Add or update a user's membership in a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "role": { + "type": "string", + "description": "Role: member, maintainer" + }, + "slug": { + "type": "string", + "description": "Team slug" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "org", + "slug", + "username" + ] + } + }, + { + "name": "github_remove_team_member", + "description": "Remove a user from a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "slug": { + "type": "string", + "description": "Team slug" + }, + "username": { + "type": "string", + "description": "GitHub username" + } + }, + "required": [ + "org", + "slug", + "username" + ] + } + }, + { + "name": "github_add_team_repo", + "description": "Add a repository to a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "permission": { + "type": "string", + "description": "Permission: pull, triage, push, maintain, admin" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "owner", + "repo", + "slug" + ] + } + }, + { + "name": "github_remove_team_repo", + "description": "Remove a repository from a team", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "slug": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "org", + "owner", + "repo", + "slug" + ] + } + }, + { + "name": "github_list_pending_org_invitations", + "description": "List pending organization invitations", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_outside_collaborators", + "description": "List outside collaborators for an organization", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter: 2fa_disabled, all" + }, + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_workflows", + "description": "List workflows in a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_workflow_runs", + "description": "List workflow runs for a repository. Use to check CI status or recent builds.", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Filter by branch" + }, + "event": { + "type": "string", + "description": "Filter by event (push, pull_request, etc.)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "status": { + "type": "string", + "description": "Filter: completed, action_required, cancelled, failure, neutral, skipped, stale, success, timed_out, in_progress, queued, requested, waiting, pending" + }, + "workflow_id": { + "type": "string", + "description": "Workflow ID or filename (e.g., ci.yml)" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_workflow_run", + "description": "Get a specific workflow run. Use after list_workflow_runs for full run details.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_list_workflow_jobs", + "description": "List jobs for a workflow run", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter: latest, all" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_download_workflow_logs", + "description": "Get a URL to download workflow run logs", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_rerun_workflow", + "description": "Re-run a workflow run", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_cancel_workflow_run", + "description": "Cancel a workflow run", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_list_repo_secrets", + "description": "List repository Actions secrets (names only, not values)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_artifacts", + "description": "List artifacts for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_environment_secrets", + "description": "List secrets for an environment (names only)", + "inputSchema": { + "type": "object", + "properties": { + "environment": { + "type": "string", + "description": "Environment name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "environment", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_org_secrets", + "description": "List organization Actions secrets (names only)", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_trigger_workflow", + "description": "Trigger a workflow dispatch event", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "ref": { + "type": "string", + "description": "Git ref to run workflow on (branch or tag)" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "workflow_id": { + "type": "string", + "description": "Workflow filename (e.g., ci.yml)" + } + }, + "required": [ + "owner", + "ref", + "repo", + "workflow_id" + ] + } + }, + { + "name": "github_rerun_failed_jobs", + "description": "Re-run only the failed jobs of a workflow run", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_get_workflow_job", + "description": "Get a single workflow job by ID", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "job_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_get_workflow_job_logs", + "description": "Get a URL to download a single job's logs", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "job_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_delete_workflow_run", + "description": "Delete a workflow run", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "run_id": { + "type": "string", + "description": "Workflow run ID" + } + }, + "required": [ + "owner", + "repo", + "run_id" + ] + } + }, + { + "name": "github_list_repo_variables", + "description": "List repository Actions variables (names and values)", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_repo_variable", + "description": "Create a repository Actions variable", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "value": { + "type": "string", + "description": "Variable value" + } + }, + "required": [ + "name", + "owner", + "repo", + "value" + ] + } + }, + { + "name": "github_update_repo_variable", + "description": "Update a repository Actions variable", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "value": { + "type": "string", + "description": "New variable value" + } + }, + "required": [ + "name", + "owner", + "repo", + "value" + ] + } + }, + { + "name": "github_delete_repo_variable", + "description": "Delete a repository Actions variable", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "name", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_org_variables", + "description": "List organization Actions variables (names and values)", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_env_variables", + "description": "List environment Actions variables (names and values)", + "inputSchema": { + "type": "object", + "properties": { + "environment": { + "type": "string", + "description": "Environment name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "environment", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_runners", + "description": "List self-hosted runners for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_runner_registration_token", + "description": "Create a repository self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token \u003cvalue\u003e command; tokens are valid for about 1 hour.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_org_runners", + "description": "List self-hosted runners for an organization", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_create_org_runner_registration_token", + "description": "Create an organization self-hosted runner registration token and expiry time. Pass the token to the runner's ./config.sh --token \u003cvalue\u003e command; tokens are valid for about 1 hour.", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_check_runs", + "description": "List check runs for a git reference", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref (SHA, branch, or tag)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_get_check_run", + "description": "Get a check run by ID", + "inputSchema": { + "type": "object", + "properties": { + "check_run_id": { + "type": "string", + "description": "Check run ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "check_run_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_check_suites", + "description": "List check suites for a git reference", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref (SHA, branch, or tag)" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "ref", + "repo" + ] + } + }, + { + "name": "github_list_releases", + "description": "List releases for a repository. Start here for release history, versioning, and what shipped.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_release", + "description": "Get a release by ID", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "release_id": { + "type": "string", + "description": "Release ID" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "release_id", + "repo" + ] + } + }, + { + "name": "github_get_latest_release", + "description": "Get the latest release for a repository. Use to find what version is current or what shipped most recently.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_release", + "description": "Create a release", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Release notes (markdown)" + }, + "draft": { + "type": "string", + "description": "Create as draft (true/false)" + }, + "name": { + "type": "string", + "description": "Release name" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "prerelease": { + "type": "string", + "description": "Mark as pre-release (true/false)" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "tag_name": { + "type": "string", + "description": "Tag name for the release" + }, + "target_commitish": { + "type": "string", + "description": "Branch or SHA to tag (defaults to default branch)" + } + }, + "required": [ + "owner", + "repo", + "tag_name" + ] + } + }, + { + "name": "github_delete_release", + "description": "Delete a release", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "release_id": { + "type": "string", + "description": "Release ID" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "release_id", + "repo" + ] + } + }, + { + "name": "github_list_release_assets", + "description": "List assets for a release", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "release_id": { + "type": "string", + "description": "Release ID" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "release_id", + "repo" + ] + } + }, + { + "name": "github_list_gists", + "description": "List gists for the authenticated user or a specific user", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "username": { + "type": "string", + "description": "GitHub username (empty for authenticated user)" + } + } + } + }, + { + "name": "github_get_gist", + "description": "Get a gist by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Gist ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "github_create_gist", + "description": "Create a gist", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "File content" + }, + "description": { + "type": "string", + "description": "Gist description" + }, + "filename": { + "type": "string", + "description": "Filename for the gist content" + }, + "public": { + "type": "string", + "description": "Public gist (true/false)" + } + }, + "required": [ + "content", + "filename" + ] + } + }, + { + "name": "github_search_topics", + "description": "Search GitHub topics", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_search_labels", + "description": "Search labels in a repository", + "inputSchema": { + "type": "object", + "properties": { + "order": { + "type": "string", + "description": "Order: asc, desc" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query" + }, + "repository_id": { + "type": "string", + "description": "Repository numeric ID" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated" + } + }, + "required": [ + "query", + "repository_id" + ] + } + }, + { + "name": "github_search_code", + "description": "Search code across GitHub repositories. Start here to find files, functions, or strings across repos.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query (supports qualifiers like language:go, repo:owner/name)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_search_issues", + "description": "Search issues and pull requests across GitHub. Start here for cross-repo issue/PR discovery. Add is:pr or is:issue to filter. Use for cross-repo bug, ticket, or PR discovery.", + "inputSchema": { + "type": "object", + "properties": { + "order": { + "type": "string", + "description": "Order: asc, desc" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query (supports qualifiers like is:issue, is:pr, repo:owner/name, state:open)" + }, + "sort": { + "type": "string", + "description": "Sort: comments, reactions, created, updated" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_search_users", + "description": "Search GitHub users", + "inputSchema": { + "type": "object", + "properties": { + "order": { + "type": "string", + "description": "Order: asc, desc" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query (supports qualifiers like location:, language:, followers:\u003eN)" + }, + "sort": { + "type": "string", + "description": "Sort: followers, repositories, joined" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_search_commits", + "description": "Search commits across GitHub", + "inputSchema": { + "type": "object", + "properties": { + "order": { + "type": "string", + "description": "Order: asc, desc" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query (supports qualifiers like author:, repo:, committer:)" + }, + "sort": { + "type": "string", + "description": "Sort: author-date, committer-date" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "github_list_stargazers", + "description": "List stargazers for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_watchers", + "description": "List watchers (subscribers) of a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_notifications", + "description": "List notifications for the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "all": { + "type": "string", + "description": "Show all notifications including read (true/false)" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "participating": { + "type": "string", + "description": "Only show participating notifications (true/false)" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "github_list_repo_events", + "description": "List events for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_mark_notifications_read", + "description": "Mark all notifications as read", + "inputSchema": { + "type": "object" + } + }, + { + "name": "github_star_repo", + "description": "Star a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_unstar_repo", + "description": "Unstar a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_list_starred", + "description": "List repositories starred by a user", + "inputSchema": { + "type": "object", + "properties": { + "direction": { + "type": "string", + "description": "Direction: asc, desc" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "sort": { + "type": "string", + "description": "Sort: created, updated" + }, + "username": { + "type": "string", + "description": "GitHub username (empty for authenticated user)" + } + } + } + }, + { + "name": "github_list_code_scanning_alerts", + "description": "List code scanning (SAST) alerts for a repository. Start here for security vulnerabilities found by CodeQL or other analyzers.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref to filter by" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "state": { + "type": "string", + "description": "State: open, closed, dismissed, fixed" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_code_scanning_alert", + "description": "Get a code scanning alert", + "inputSchema": { + "type": "object", + "properties": { + "alert_number": { + "type": "string", + "description": "Alert number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "alert_number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_secret_scanning_alerts", + "description": "List secret scanning alerts for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "secret_type": { + "type": "string", + "description": "Filter by secret type" + }, + "state": { + "type": "string", + "description": "State: open, resolved" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_secret_scanning_alert", + "description": "Get a single secret scanning alert", + "inputSchema": { + "type": "object", + "properties": { + "alert_number": { + "type": "string", + "description": "Alert number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "alert_number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_dependabot_alerts", + "description": "List Dependabot dependency vulnerability alerts for a repository. Start here for CVE impact on dependencies.", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "severity": { + "type": "string", + "description": "Severity: low, medium, high, critical" + }, + "state": { + "type": "string", + "description": "State: auto_dismissed, dismissed, fixed, open" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_dependabot_alert", + "description": "Get a single Dependabot alert", + "inputSchema": { + "type": "object", + "properties": { + "alert_number": { + "type": "string", + "description": "Alert number" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "alert_number", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_code_scanning_analyses", + "description": "List code scanning SARIF analyses for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "ref": { + "type": "string", + "description": "Git ref to filter by" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_sbom", + "description": "Get the software bill of materials (SBOM) for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_copilot_org_usage", + "description": "Get Copilot usage metrics for an organization", + "inputSchema": { + "type": "object", + "properties": { + "org": { + "type": "string", + "description": "Organization name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "org" + ] + } + }, + { + "name": "github_list_hooks", + "description": "List webhooks for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_create_hook", + "description": "Create a webhook for a repository", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Active (true/false, default true)" + }, + "content_type": { + "type": "string", + "description": "Content type: json, form" + }, + "events": { + "type": "string", + "description": "Comma-separated events (push, pull_request, issues, etc.)" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + }, + "url": { + "type": "string", + "description": "Webhook payload URL" + } + }, + "required": [ + "owner", + "repo", + "url" + ] + } + }, + { + "name": "github_delete_hook", + "description": "Delete a webhook", + "inputSchema": { + "type": "object", + "properties": { + "hook_id": { + "type": "string", + "description": "Webhook ID" + }, + "owner": { + "type": "string", + "description": "Repository owner" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "hook_id", + "owner", + "repo" + ] + } + }, + { + "name": "github_list_deploy_keys", + "description": "List deploy keys for a repository", + "inputSchema": { + "type": "object", + "properties": { + "owner": { + "type": "string", + "description": "Repository owner" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "repo": { + "type": "string", + "description": "Repository name" + } + }, + "required": [ + "owner", + "repo" + ] + } + }, + { + "name": "github_get_rate_limit", + "description": "Get API rate limit status for the authenticated user", + "inputSchema": { + "type": "object" + } + }, + { + "name": "datadog_search_logs", + "description": "Search Datadog logs for production debugging and observability. Find errors, traces, and events by query string.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time (ISO 8601, epoch seconds, or relative like 'now-1h')" + }, + "limit": { + "type": "string", + "description": "Max results (default 50, max 1000)" + }, + "query": { + "type": "string", + "description": "Log search query (e.g., 'service:nginx status:error')" + }, + "sort": { + "type": "string", + "description": "Sort order: timestamp (asc) or -timestamp (desc, default)" + }, + "to": { + "type": "string", + "description": "End time (default: now)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_aggregate_logs", + "description": "Aggregate Datadog logs for monitoring analytics (count, sum, avg, etc.). Use for production observability and trend analysis.", + "inputSchema": { + "type": "object", + "properties": { + "compute_field": { + "type": "string", + "description": "Field to aggregate on (required for non-count types, e.g., @duration)" + }, + "compute_type": { + "type": "string", + "description": "Aggregation type: count, cardinality, avg, sum, min, max, percentile" + }, + "from": { + "type": "string", + "description": "Start time" + }, + "group_by": { + "type": "string", + "description": "Field to group by (e.g., service, @http.status_code)" + }, + "query": { + "type": "string", + "description": "Log search query" + }, + "to": { + "type": "string", + "description": "End time" + } + }, + "required": [ + "compute_type", + "query" + ] + } + }, + { + "name": "datadog_query_metrics", + "description": "Query Datadog metrics timeseries data for production monitoring. Analyze performance, CPU, memory, latency, and custom metrics.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time (epoch seconds or relative)" + }, + "query": { + "type": "string", + "description": "Metrics query (e.g., 'avg:system.cpu.user{*}')" + }, + "to": { + "type": "string", + "description": "End time" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_list_active_metrics", + "description": "List actively reporting metrics from a given time", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time (epoch seconds, default: now-1h)" + }, + "host": { + "type": "string", + "description": "Filter by host" + }, + "tag_filter": { + "type": "string", + "description": "Filter by tag (e.g., env:prod)" + } + } + } + }, + { + "name": "datadog_search_metrics", + "description": "Search for metrics by name", + "inputSchema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Metric name query (e.g., 'system.cpu')" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_get_metric_metadata", + "description": "Get metadata for a specific metric", + "inputSchema": { + "type": "object", + "properties": { + "metric": { + "type": "string", + "description": "Metric name (e.g., system.cpu.user)" + } + }, + "required": [ + "metric" + ] + } + }, + { + "name": "datadog_list_monitors", + "description": "List Datadog monitors for production alerting and observability. Filter by status, tags, or environment.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number (0-based)" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 100)" + }, + "query": { + "type": "string", + "description": "Filter query (e.g., 'status:alert tag:env:prod')" + } + } + } + }, + { + "name": "datadog_search_monitors", + "description": "Search Datadog monitors and alerts by query string. Find production monitoring rules and notification policies.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 30)" + }, + "query": { + "type": "string", + "description": "Search query (e.g., 'type:metric status:alert')" + } + } + } + }, + { + "name": "datadog_get_monitor", + "description": "Get a specific monitor by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Monitor ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_monitor", + "description": "Create a new Datadog monitor for production alerting. Set thresholds and notification rules for metrics, logs, or services.", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Notification message (supports @mentions)" + }, + "name": { + "type": "string", + "description": "Monitor name" + }, + "priority": { + "type": "string", + "description": "Priority (1-5)" + }, + "query": { + "type": "string", + "description": "Monitor query" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + }, + "type": { + "type": "string", + "description": "Monitor type: metric alert, service check, event alert, query alert, composite, log alert, etc." + } + }, + "required": [ + "name", + "query", + "type" + ] + } + }, + { + "name": "datadog_update_monitor", + "description": "Update an existing monitor", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Monitor ID" + }, + "message": { + "type": "string", + "description": "New message" + }, + "name": { + "type": "string", + "description": "New name" + }, + "priority": { + "type": "string", + "description": "Priority (1-5)" + }, + "query": { + "type": "string", + "description": "New query" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_delete_monitor", + "description": "Delete a monitor", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Monitor ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_mute_monitor", + "description": "Mute a monitor (silence notifications)", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End timestamp (epoch seconds, omit for indefinite)" + }, + "id": { + "type": "string", + "description": "Monitor ID" + }, + "scope": { + "type": "string", + "description": "Scope to mute (e.g., 'host:myhost')" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_dashboards", + "description": "List all Datadog dashboards for production monitoring and observability visualization", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "description": "Max results (default 100)" + }, + "filter_deleted": { + "type": "string", + "description": "Include deleted (true/false)" + }, + "filter_shared": { + "type": "string", + "description": "Filter shared dashboards (true/false)" + }, + "start": { + "type": "string", + "description": "Offset for pagination" + } + } + } + }, + { + "name": "datadog_get_dashboard", + "description": "Get a specific dashboard by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Dashboard ID (e.g., 'abc-def-ghi')" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_dashboard", + "description": "Create a new dashboard (JSON body)", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Dashboard description" + }, + "layout_type": { + "type": "string", + "description": "Layout: ordered or free" + }, + "title": { + "type": "string", + "description": "Dashboard title" + }, + "widgets_json": { + "type": "string", + "description": "JSON array of widget definitions" + } + }, + "required": [ + "layout_type", + "title" + ] + } + }, + { + "name": "datadog_delete_dashboard", + "description": "Delete a dashboard", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_events", + "description": "List Datadog events for production monitoring. Track deployments, changes, alerts, and system events.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time" + }, + "limit": { + "type": "string", + "description": "Max results (default 10)" + }, + "query": { + "type": "string", + "description": "Event search query" + }, + "sort": { + "type": "string", + "description": "Sort: timestamp (asc) or -timestamp (desc)" + }, + "to": { + "type": "string", + "description": "End time" + } + } + } + }, + { + "name": "datadog_search_events", + "description": "Search Datadog events by query. Find production changes, deployments, and system events.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time" + }, + "limit": { + "type": "string", + "description": "Max results (default 10)" + }, + "query": { + "type": "string", + "description": "Event search query" + }, + "sort": { + "type": "string", + "description": "Sort: timestamp or -timestamp" + }, + "to": { + "type": "string", + "description": "End time" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_get_event", + "description": "Get a specific event by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Event ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_event", + "description": "Create a new event", + "inputSchema": { + "type": "object", + "properties": { + "aggregation_key": { + "type": "string", + "description": "Aggregation key for grouping" + }, + "alert_type": { + "type": "string", + "description": "Type: error, warning, info, success, user_update, recommendation, snapshot" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + }, + "text": { + "type": "string", + "description": "Event text/body" + }, + "title": { + "type": "string", + "description": "Event title" + } + }, + "required": [ + "text", + "title" + ] + } + }, + { + "name": "datadog_list_hosts", + "description": "List Datadog hosts (servers and infrastructure). Filter production machines by environment, CPU, load, or tags.", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "description": "Max results (default 100)" + }, + "filter": { + "type": "string", + "description": "Filter string (e.g., 'env:prod')" + }, + "from": { + "type": "string", + "description": "Seconds since hosts last reported" + }, + "sort_dir": { + "type": "string", + "description": "Sort direction: asc or desc" + }, + "sort_field": { + "type": "string", + "description": "Sort by: apps, cpu, iowait, load, etc." + } + } + } + }, + { + "name": "datadog_get_host_totals", + "description": "Get total number of hosts", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Seconds since hosts last reported" + } + } + } + }, + { + "name": "datadog_mute_host", + "description": "Mute a host", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End timestamp (epoch seconds)" + }, + "hostname": { + "type": "string", + "description": "Hostname to mute" + }, + "message": { + "type": "string", + "description": "Mute reason" + }, + "override": { + "type": "string", + "description": "Override existing mute (true/false)" + } + }, + "required": [ + "hostname" + ] + } + }, + { + "name": "datadog_unmute_host", + "description": "Unmute a host", + "inputSchema": { + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "Hostname to unmute" + } + }, + "required": [ + "hostname" + ] + } + }, + { + "name": "datadog_list_tags", + "description": "List all host tags", + "inputSchema": { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Tag source (e.g., datadog-agent, chef, users)" + } + } + } + }, + { + "name": "datadog_get_host_tags", + "description": "Get tags for a specific host", + "inputSchema": { + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "Hostname" + }, + "source": { + "type": "string", + "description": "Tag source" + } + }, + "required": [ + "hostname" + ] + } + }, + { + "name": "datadog_create_host_tags", + "description": "Add tags to a host", + "inputSchema": { + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "Hostname" + }, + "source": { + "type": "string", + "description": "Tag source" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags (e.g., 'env:prod,role:web')" + } + }, + "required": [ + "hostname", + "tags" + ] + } + }, + { + "name": "datadog_update_host_tags", + "description": "Replace all tags on a host", + "inputSchema": { + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "Hostname" + }, + "source": { + "type": "string", + "description": "Tag source" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "hostname", + "tags" + ] + } + }, + { + "name": "datadog_delete_host_tags", + "description": "Remove tags from a host", + "inputSchema": { + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "Hostname" + }, + "source": { + "type": "string", + "description": "Tag source" + } + }, + "required": [ + "hostname" + ] + } + }, + { + "name": "datadog_list_slos", + "description": "List Service Level Objectives", + "inputSchema": { + "type": "object", + "properties": { + "ids": { + "type": "string", + "description": "Comma-separated SLO IDs" + }, + "limit": { + "type": "string", + "description": "Max results (default 1000)" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "query": { + "type": "string", + "description": "Filter query (e.g., 'name:my-slo')" + }, + "tags_query": { + "type": "string", + "description": "Filter by tags (e.g., 'env:prod')" + } + } + } + }, + { + "name": "datadog_search_slos", + "description": "Search SLOs", + "inputSchema": { + "type": "object", + "properties": { + "page_number": { + "type": "string", + "description": "Page number" + }, + "page_size": { + "type": "string", + "description": "Results per page" + }, + "query": { + "type": "string", + "description": "Search query" + } + } + } + }, + { + "name": "datadog_get_slo", + "description": "Get a specific SLO by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "SLO ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_get_slo_history", + "description": "Get SLO history data", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time (epoch seconds)" + }, + "id": { + "type": "string", + "description": "SLO ID" + }, + "to": { + "type": "string", + "description": "End time (epoch seconds)" + } + }, + "required": [ + "from", + "id", + "to" + ] + } + }, + { + "name": "datadog_create_slo", + "description": "Create a new SLO", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description" + }, + "monitor_ids": { + "type": "string", + "description": "Comma-separated monitor IDs (for monitor type)" + }, + "name": { + "type": "string", + "description": "SLO name" + }, + "query_denominator": { + "type": "string", + "description": "Total events query (for metric type)" + }, + "query_numerator": { + "type": "string", + "description": "Good events query (for metric type)" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + }, + "target": { + "type": "string", + "description": "Target percentage (e.g., 99.9)" + }, + "timeframe": { + "type": "string", + "description": "Timeframe: 7d, 30d, 90d" + }, + "type": { + "type": "string", + "description": "Type: metric or monitor" + } + }, + "required": [ + "name", + "target", + "timeframe", + "type" + ] + } + }, + { + "name": "datadog_delete_slo", + "description": "Delete a SLO", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "SLO ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_downtimes", + "description": "List scheduled downtimes", + "inputSchema": { + "type": "object", + "properties": { + "current_only": { + "type": "string", + "description": "Only show current downtimes (true/false)" + } + } + } + }, + { + "name": "datadog_get_downtime", + "description": "Get a specific downtime by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Downtime ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_downtime", + "description": "Schedule a downtime", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time (epoch seconds)" + }, + "message": { + "type": "string", + "description": "Message/reason" + }, + "monitor_identifier_id": { + "type": "string", + "description": "Monitor ID (when type=id)" + }, + "monitor_identifier_tags": { + "type": "string", + "description": "Monitor tags (when type=tags, comma-separated)" + }, + "monitor_identifier_type": { + "type": "string", + "description": "Type: id or tags" + }, + "scope": { + "type": "string", + "description": "Scope (e.g., 'env:prod', 'host:myhost')" + }, + "start": { + "type": "string", + "description": "Start time (epoch seconds, default: now)" + } + }, + "required": [ + "scope" + ] + } + }, + { + "name": "datadog_cancel_downtime", + "description": "Cancel a scheduled downtime", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Downtime ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_incidents", + "description": "List Datadog incidents for production outages and service disruptions. Track severity and incident response.", + "inputSchema": { + "type": "object", + "properties": { + "page_offset": { + "type": "string", + "description": "Pagination offset" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + } + } + } + }, + { + "name": "datadog_search_incidents", + "description": "Search Datadog incidents by query. Find production outages, disruptions, and postmortems by severity, status, or team.", + "inputSchema": { + "type": "object", + "properties": { + "page_offset": { + "type": "string", + "description": "Pagination offset" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + }, + "query": { + "type": "string", + "description": "Incident search query (e.g., 'state:active AND severity:SEV-1')" + }, + "sort": { + "type": "string", + "description": "Sort order: created or -created (default)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_get_incident", + "description": "Get details of a specific Datadog incident, including timeline and response data for outage investigation", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Incident ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_incident", + "description": "Create a new incident", + "inputSchema": { + "type": "object", + "properties": { + "customer_impacted": { + "type": "string", + "description": "Customer impacted (true/false)" + }, + "severity": { + "type": "string", + "description": "Severity: SEV-1, SEV-2, SEV-3, SEV-4, SEV-5, UNKNOWN" + }, + "title": { + "type": "string", + "description": "Incident title" + } + }, + "required": [ + "customer_impacted", + "title" + ] + } + }, + { + "name": "datadog_update_incident", + "description": "Update an incident", + "inputSchema": { + "type": "object", + "properties": { + "customer_impacted": { + "type": "string", + "description": "Customer impacted (true/false)" + }, + "id": { + "type": "string", + "description": "Incident ID" + }, + "severity": { + "type": "string", + "description": "New severity" + }, + "status": { + "type": "string", + "description": "Status: active, stable, resolved" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_incident_attachments", + "description": "List attachments for a Datadog incident. View postmortems and linked resources attached to an outage.", + "inputSchema": { + "type": "object", + "properties": { + "incident_id": { + "type": "string", + "description": "Incident ID" + } + }, + "required": [ + "incident_id" + ] + } + }, + { + "name": "datadog_list_incident_todos", + "description": "List todos and action items for a Datadog incident. Track remediation tasks and follow-up work.", + "inputSchema": { + "type": "object", + "properties": { + "incident_id": { + "type": "string", + "description": "Incident ID" + } + }, + "required": [ + "incident_id" + ] + } + }, + { + "name": "datadog_list_incident_services", + "description": "List Datadog incident services. View services configured for incident management and response.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter services by name" + }, + "page_offset": { + "type": "string", + "description": "Pagination offset" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + } + } + } + }, + { + "name": "datadog_get_incident_service", + "description": "Get details of a specific incident service. Use after list_incident_services.", + "inputSchema": { + "type": "object", + "properties": { + "service_id": { + "type": "string", + "description": "Incident service ID" + } + }, + "required": [ + "service_id" + ] + } + }, + { + "name": "datadog_create_incident_service", + "description": "Create a new incident service for incident management", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Service name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "datadog_update_incident_service", + "description": "Update an existing incident service", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "New service name" + }, + "service_id": { + "type": "string", + "description": "Incident service ID" + } + }, + "required": [ + "name", + "service_id" + ] + } + }, + { + "name": "datadog_delete_incident_service", + "description": "Delete an incident service", + "inputSchema": { + "type": "object", + "properties": { + "service_id": { + "type": "string", + "description": "Incident service ID" + } + }, + "required": [ + "service_id" + ] + } + }, + { + "name": "datadog_list_incident_teams", + "description": "List Datadog incident teams. View teams configured for incident response and on-call rotation.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter teams by name" + }, + "page_offset": { + "type": "string", + "description": "Pagination offset" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + } + } + } + }, + { + "name": "datadog_get_incident_team", + "description": "Get details of a specific incident team. Use after list_incident_teams.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Incident team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_create_incident_team", + "description": "Create a new incident team for incident response", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Team name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "datadog_update_incident_team", + "description": "Update an existing incident team", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "New team name" + }, + "team_id": { + "type": "string", + "description": "Incident team ID" + } + }, + "required": [ + "name", + "team_id" + ] + } + }, + { + "name": "datadog_delete_incident_team", + "description": "Delete an incident team", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Incident team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_list_synthetics_tests", + "description": "List synthetic monitoring tests", + "inputSchema": { + "type": "object", + "properties": { + "page_number": { + "type": "string", + "description": "Page number" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 100)" + } + } + } + }, + { + "name": "datadog_get_synthetics_api_test", + "description": "Get a specific synthetics API test", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Test public ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_get_synthetics_test_result", + "description": "Get latest results for a synthetics test", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Test public ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_trigger_synthetics_tests", + "description": "Trigger synthetic tests on demand", + "inputSchema": { + "type": "object", + "properties": { + "public_ids": { + "type": "string", + "description": "Comma-separated test public IDs to trigger" + } + }, + "required": [ + "public_ids" + ] + } + }, + { + "name": "datadog_list_notebooks", + "description": "List Datadog notebooks", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "description": "Max results (default 100)" + }, + "query": { + "type": "string", + "description": "Search query" + }, + "sort_dir": { + "type": "string", + "description": "Sort direction: asc or desc" + }, + "sort_field": { + "type": "string", + "description": "Sort by: modified or name" + }, + "start": { + "type": "string", + "description": "Offset for pagination" + } + } + } + }, + { + "name": "datadog_get_notebook", + "description": "Get a specific notebook by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Notebook ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_create_notebook", + "description": "Create a new notebook (JSON body)", + "inputSchema": { + "type": "object", + "properties": { + "cells_json": { + "type": "string", + "description": "JSON array of notebook cells" + }, + "name": { + "type": "string", + "description": "Notebook name" + }, + "time_from": { + "type": "string", + "description": "Time range start" + }, + "time_to": { + "type": "string", + "description": "Time range end" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "datadog_delete_notebook", + "description": "Delete a notebook", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Notebook ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_users", + "description": "List users in the organization", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter string" + }, + "page_number": { + "type": "string", + "description": "Page number" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + }, + "sort": { + "type": "string", + "description": "Sort field (e.g., name, email)" + } + } + } + }, + { + "name": "datadog_get_user", + "description": "Get a specific user by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "datadog_list_teams", + "description": "List Datadog teams. View organizational teams, ownership, and membership. Start here for team management.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter teams by keyword/name" + }, + "page_number": { + "type": "string", + "description": "Page number" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + }, + "sort": { + "type": "string", + "description": "Sort: name, -name, user_count, -user_count" + } + } + } + }, + { + "name": "datadog_get_team", + "description": "Get details of a specific Datadog team including handle, description, and member count. Use after list_teams.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_create_team", + "description": "Create a new Datadog team", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Team description" + }, + "handle": { + "type": "string", + "description": "Team handle (URL-safe identifier)" + }, + "name": { + "type": "string", + "description": "Team name" + } + }, + "required": [ + "handle", + "name" + ] + } + }, + { + "name": "datadog_update_team", + "description": "Update an existing team", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "handle": { + "type": "string", + "description": "New team handle" + }, + "name": { + "type": "string", + "description": "New team name" + }, + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "handle", + "name", + "team_id" + ] + } + }, + { + "name": "datadog_delete_team", + "description": "Delete a team", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_list_team_members", + "description": "List members of a Datadog team. View who belongs to a team and their roles.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter members by keyword" + }, + "page_number": { + "type": "string", + "description": "Page number" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 10)" + }, + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_add_team_member", + "description": "Add a user to a team", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role: admin (omit for regular member)" + }, + "team_id": { + "type": "string", + "description": "Team ID" + }, + "user_id": { + "type": "string", + "description": "User ID to add" + } + }, + "required": [ + "team_id", + "user_id" + ] + } + }, + { + "name": "datadog_update_team_member", + "description": "Update a team member's role", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "New role: admin (omit for regular member)" + }, + "team_id": { + "type": "string", + "description": "Team ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "team_id", + "user_id" + ] + } + }, + { + "name": "datadog_remove_team_member", + "description": "Remove a user from a team", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + }, + "user_id": { + "type": "string", + "description": "User ID to remove" + } + }, + "required": [ + "team_id", + "user_id" + ] + } + }, + { + "name": "datadog_get_user_team_memberships", + "description": "Get all team memberships for a user. Find which teams a user belongs to.", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "datadog_list_team_links", + "description": "List external links for a team. View runbooks, dashboards, and documentation links associated with a team.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_get_team_link", + "description": "Get a specific team link. Use after list_team_links.", + "inputSchema": { + "type": "object", + "properties": { + "link_id": { + "type": "string", + "description": "Link ID" + }, + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "link_id", + "team_id" + ] + } + }, + { + "name": "datadog_create_team_link", + "description": "Add a link to a team (runbook, dashboard, documentation)", + "inputSchema": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Link label" + }, + "team_id": { + "type": "string", + "description": "Team ID" + }, + "url": { + "type": "string", + "description": "Link URL" + } + }, + "required": [ + "label", + "team_id", + "url" + ] + } + }, + { + "name": "datadog_update_team_link", + "description": "Update a team link", + "inputSchema": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "New label" + }, + "link_id": { + "type": "string", + "description": "Link ID" + }, + "team_id": { + "type": "string", + "description": "Team ID" + }, + "url": { + "type": "string", + "description": "New URL" + } + }, + "required": [ + "label", + "link_id", + "team_id", + "url" + ] + } + }, + { + "name": "datadog_delete_team_link", + "description": "Delete a team link", + "inputSchema": { + "type": "object", + "properties": { + "link_id": { + "type": "string", + "description": "Link ID" + }, + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "link_id", + "team_id" + ] + } + }, + { + "name": "datadog_get_team_permission_settings", + "description": "Get permission settings for a team. View who can manage membership and edit the team.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_update_team_permission_setting", + "description": "Update a team permission setting", + "inputSchema": { + "type": "object", + "properties": { + "action": { + "type": "string", + "description": "Permission action: manage_membership or edit" + }, + "team_id": { + "type": "string", + "description": "Team ID" + }, + "value": { + "type": "string", + "description": "Permission value: admins, members, organization, user_access_manage, teams_manage" + } + }, + "required": [ + "action", + "team_id", + "value" + ] + } + }, + { + "name": "datadog_search_spans", + "description": "Search Datadog APM spans and distributed traces for production performance debugging. Investigate latency and service dependencies.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Start time" + }, + "limit": { + "type": "string", + "description": "Max results (default 10)" + }, + "query": { + "type": "string", + "description": "Span search query (e.g., 'service:web-store resource_name:GET')" + }, + "sort": { + "type": "string", + "description": "Sort: timestamp or -timestamp" + }, + "to": { + "type": "string", + "description": "End time" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "datadog_list_services", + "description": "List services from the Datadog Software Catalog. View production microservices, dependencies, and ownership.", + "inputSchema": { + "type": "object", + "properties": { + "page_offset": { + "type": "string", + "description": "Pagination offset" + }, + "page_size": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "datadog_list_oncall_schedules", + "description": "List all Datadog On-Call schedules. Start here to find schedule IDs before getting details.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include (e.g. teams)" + } + } + } + }, + { + "name": "datadog_get_oncall_schedule", + "description": "Get a Datadog On-Call schedule with layers, members, and rotation details. View who is on-call and when.", + "inputSchema": { + "type": "object", + "properties": { + "schedule_id": { + "type": "string", + "description": "On-Call schedule ID" + } + }, + "required": [ + "schedule_id" + ] + } + }, + { + "name": "datadog_create_oncall_schedule", + "description": "Create a new On-Call schedule with layers, rotation intervals, and members. Use body_json with Datadog schedule create schema.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog ScheduleCreateRequest schema: {\"data\":{\"type\":\"schedules\",\"attributes\":{\"name\":\"...\",\"time_zone\":\"...\",\"layers\":[{\"name\":\"...\",\"effective_date\":\"...\",\"rotation_start\":\"...\",\"interval\":{\"days\":7},\"members\":[{\"user\":{\"id\":\"...\"}}]}]}}}" + } + }, + "required": [ + "body_json" + ] + } + }, + { + "name": "datadog_update_oncall_schedule", + "description": "Update an existing On-Call schedule (layers, members, rotations). IMPORTANT: Always include the full relationships.teams block in body_json — omitting it silently removes the team association. Fetch current schedule with get_oncall_schedule first, then modify and submit.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog ScheduleUpdateRequest schema. MUST include relationships.teams to preserve team association." + }, + "schedule_id": { + "type": "string", + "description": "Schedule ID" + } + }, + "required": [ + "body_json", + "schedule_id" + ] + } + }, + { + "name": "datadog_delete_oncall_schedule", + "description": "Delete an On-Call schedule", + "inputSchema": { + "type": "object", + "properties": { + "schedule_id": { + "type": "string", + "description": "Schedule ID" + } + }, + "required": [ + "schedule_id" + ] + } + }, + { + "name": "datadog_get_schedule_oncall_user", + "description": "Get the current on-call user for a schedule. Find who is on-call right now for a given rotation.", + "inputSchema": { + "type": "object", + "properties": { + "schedule_id": { + "type": "string", + "description": "On-Call schedule ID" + } + }, + "required": [ + "schedule_id" + ] + } + }, + { + "name": "datadog_list_oncall_escalation_policies", + "description": "List all Datadog On-Call escalation policies. Start here to find policy IDs before getting details.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include (e.g. teams,steps,steps.targets)" + } + } + } + }, + { + "name": "datadog_get_oncall_escalation_policy", + "description": "Get a Datadog On-Call escalation policy with steps, targets, and notification chain. View escalation rules and timing.", + "inputSchema": { + "type": "object", + "properties": { + "policy_id": { + "type": "string", + "description": "Escalation policy ID" + } + }, + "required": [ + "policy_id" + ] + } + }, + { + "name": "datadog_create_oncall_escalation_policy", + "description": "Create a new On-Call escalation policy with steps and targets. Use body_json with Datadog escalation policy create schema.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog EscalationPolicyCreateRequest schema: {\"data\":{\"type\":\"policies\",\"attributes\":{\"name\":\"...\",\"steps\":[{\"targets\":[{\"type\":\"users\",\"id\":\"...\"}],\"escalate_after_seconds\":300}]}}}" + } + }, + "required": [ + "body_json" + ] + } + }, + { + "name": "datadog_update_oncall_escalation_policy", + "description": "Update an existing On-Call escalation policy (steps, targets, timing). Fetch current policy with get_oncall_escalation_policy first, then modify and submit.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog EscalationPolicyUpdateRequest schema" + }, + "policy_id": { + "type": "string", + "description": "Escalation policy ID" + } + }, + "required": [ + "body_json", + "policy_id" + ] + } + }, + { + "name": "datadog_delete_oncall_escalation_policy", + "description": "Delete an On-Call escalation policy", + "inputSchema": { + "type": "object", + "properties": { + "policy_id": { + "type": "string", + "description": "Escalation policy ID" + } + }, + "required": [ + "policy_id" + ] + } + }, + { + "name": "datadog_get_oncall_team_routing_rules", + "description": "Get On-Call routing rules for a team. View how pages are routed to schedules and escalation policies.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_set_oncall_team_routing_rules", + "description": "Set (replace) On-Call routing rules for a team. Define how pages are routed to escalation policies. Fetch current rules with get_oncall_team_routing_rules first.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog TeamRoutingRulesRequest schema: {\"data\":{\"type\":\"team_routing_rules\",\"attributes\":{\"rules\":[{\"policy_id\":\"...\",\"urgency\":\"high\"}]}}}" + }, + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "body_json", + "team_id" + ] + } + }, + { + "name": "datadog_get_team_oncall_users", + "description": "Get the current on-call users for a team. Find who is on-call right now across all team schedules.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team ID" + } + }, + "required": [ + "team_id" + ] + } + }, + { + "name": "datadog_list_oncall_pages", + "description": "List On-Call pages. Filter by status and urgency to find active or past pages.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include" + }, + "status": { + "type": "string", + "description": "Filter by page status (e.g. triggered, acknowledged, resolved)" + }, + "urgency": { + "type": "string", + "description": "Filter by urgency (low or high)" + } + } + } + }, + { + "name": "datadog_get_oncall_page", + "description": "Get details of a specific On-Call page including status, responders, and timeline.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include" + }, + "page_id": { + "type": "string", + "description": "Page UUID" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "datadog_create_oncall_page", + "description": "Create a new On-Call page to alert responders. Page a team or user for production incidents and urgent issues.", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Page description with details" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + }, + "target_id": { + "type": "string", + "description": "Target identifier (team handle, team ID, or user ID)" + }, + "target_type": { + "type": "string", + "description": "Target type: team_handle, team_id, or user_id (default team_handle)" + }, + "title": { + "type": "string", + "description": "Page title" + }, + "urgency": { + "type": "string", + "description": "Urgency: low or high (default high)" + } + }, + "required": [ + "target_id", + "title" + ] + } + }, + { + "name": "datadog_acknowledge_oncall_page", + "description": "Acknowledge an On-Call page to indicate responder awareness", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "Page UUID" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "datadog_escalate_oncall_page", + "description": "Escalate an On-Call page to the next responder in the escalation policy", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "Page UUID" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "datadog_resolve_oncall_page", + "description": "Resolve an On-Call page to mark the issue as handled", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "Page UUID" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "datadog_list_user_notification_channels", + "description": "List On-Call notification channels for a user (email, Slack, SMS, push). View how a user receives on-call alerts.", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "datadog_create_user_notification_channel", + "description": "Create a notification channel for a user (email, Slack, SMS, push).", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog CreateUserNotificationChannelRequest schema" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "body_json", + "user_id" + ] + } + }, + { + "name": "datadog_get_user_notification_channel", + "description": "Get a specific notification channel for a user.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Notification channel ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "channel_id", + "user_id" + ] + } + }, + { + "name": "datadog_delete_user_notification_channel", + "description": "Delete a notification channel for a user.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Notification channel ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "channel_id", + "user_id" + ] + } + }, + { + "name": "datadog_list_user_notification_rules", + "description": "List On-Call notification rules for a user. Rules define when and how a user is notified for on-call pages.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "datadog_create_user_notification_rule", + "description": "Create a notification rule for a user defining when and how they are notified for on-call pages.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog CreateOnCallNotificationRuleRequest schema" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "body_json", + "user_id" + ] + } + }, + { + "name": "datadog_get_user_notification_rule", + "description": "Get a specific notification rule for a user.", + "inputSchema": { + "type": "object", + "properties": { + "include": { + "type": "string", + "description": "Comma-separated related resources to include" + }, + "rule_id": { + "type": "string", + "description": "Notification rule ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "rule_id", + "user_id" + ] + } + }, + { + "name": "datadog_update_user_notification_rule", + "description": "Update a notification rule for a user.", + "inputSchema": { + "type": "object", + "properties": { + "body_json": { + "type": "string", + "description": "JSON body matching Datadog UpdateOnCallNotificationRuleRequest schema" + }, + "include": { + "type": "string", + "description": "Comma-separated related resources to include" + }, + "rule_id": { + "type": "string", + "description": "Notification rule ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "body_json", + "rule_id", + "user_id" + ] + } + }, + { + "name": "datadog_delete_user_notification_rule", + "description": "Delete a notification rule for a user.", + "inputSchema": { + "type": "object", + "properties": { + "rule_id": { + "type": "string", + "description": "Notification rule ID" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "rule_id", + "user_id" + ] + } + }, + { + "name": "datadog_get_ip_ranges", + "description": "Get Datadog IP address ranges used for inbound/outbound traffic", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_list_issues", + "description": "List Linear issues (tickets/bugs/tasks) with optional filters. Start here for filtered queries (by assignee, state, label, project). Use list_workflow_states to discover valid state names.", + "inputSchema": { + "type": "object", + "properties": { + "after": { + "type": "string", + "description": "Pagination cursor" + }, + "assignee": { + "type": "string", + "description": "Filter by assignee name or 'me'" + }, + "cycle": { + "type": "string", + "description": "Filter by cycle name or number" + }, + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "label": { + "type": "string", + "description": "Filter by label name" + }, + "priority": { + "type": "string", + "description": "Filter by priority (1=urgent, 2=high, 3=normal, 4=low)" + }, + "project": { + "type": "string", + "description": "Filter by project name" + }, + "state": { + "type": "string", + "description": "Filter by state name (e.g., 'In Progress', 'Done')" + }, + "team": { + "type": "string", + "description": "Filter by team name or key" + } + } + } + }, + { + "name": "linear_search_issues", + "description": "Full-text search Linear issues (tickets/bugs) by keyword. Start here to find issues by text. For filtering by assignee, state, or label, prefer list_issues instead.", + "inputSchema": { + "type": "object", + "properties": { + "after": { + "type": "string", + "description": "Pagination cursor" + }, + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "query": { + "type": "string", + "description": "Search query text" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "linear_get_issue", + "description": "Get a specific issue with full detail. Accepts issue ID (UUID) or identifier (e.g., ENG-123).", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Issue identifier (e.g., ENG-123) or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_issue", + "description": "Create a new issue (ticket/bug/task). Requires team_id — use list_teams to find it. Use list_workflow_states to discover valid state names.", + "inputSchema": { + "type": "object", + "properties": { + "assignee": { + "type": "string", + "description": "Assignee name or email" + }, + "cycle": { + "type": "string", + "description": "Cycle name or number" + }, + "description": { + "type": "string", + "description": "Description (markdown)" + }, + "due_date": { + "type": "string", + "description": "Due date (YYYY-MM-DD)" + }, + "estimate": { + "type": "string", + "description": "Story point estimate" + }, + "labels": { + "type": "string", + "description": "Comma-separated label names" + }, + "milestone": { + "type": "string", + "description": "Project milestone name or UUID" + }, + "parent_id": { + "type": "string", + "description": "Parent issue ID for sub-issues" + }, + "priority": { + "type": "string", + "description": "Priority (0=none, 1=urgent, 2=high, 3=normal, 4=low)" + }, + "project": { + "type": "string", + "description": "Project name" + }, + "state": { + "type": "string", + "description": "Workflow state name" + }, + "team": { + "type": "string", + "description": "Team name or key" + }, + "title": { + "type": "string", + "description": "Issue title" + } + }, + "required": [ + "team", + "title" + ] + } + }, + { + "name": "linear_update_issue", + "description": "Update an existing issue. Accepts issue ID (UUID) or identifier (e.g., ENG-123). Use list_workflow_states to discover valid state names. Use list_teams to find team names for transfers.", + "inputSchema": { + "type": "object", + "properties": { + "assignee": { + "type": "string", + "description": "Assignee name or email" + }, + "description": { + "type": "string", + "description": "New description" + }, + "due_date": { + "type": "string", + "description": "Due date (YYYY-MM-DD)" + }, + "estimate": { + "type": "string", + "description": "Story point estimate" + }, + "id": { + "type": "string", + "description": "Issue identifier (e.g., ENG-123) or UUID" + }, + "labels": { + "type": "string", + "description": "Comma-separated label names" + }, + "milestone": { + "type": "string", + "description": "Project milestone name or UUID" + }, + "priority": { + "type": "string", + "description": "Priority (0-4)" + }, + "project": { + "type": "string", + "description": "Project name" + }, + "state": { + "type": "string", + "description": "Workflow state name" + }, + "team": { + "type": "string", + "description": "Team name or key (moves issue to this team)" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_archive_issue", + "description": "Archive an issue", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_unarchive_issue", + "description": "Unarchive an issue", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_issue_comments", + "description": "List comments on an issue", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_comment", + "description": "Create a comment on an issue", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Comment body (markdown)" + }, + "issue_id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "body", + "issue_id" + ] + } + }, + { + "name": "linear_update_comment", + "description": "Update a comment", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "New comment body (markdown)" + }, + "id": { + "type": "string", + "description": "Comment UUID" + } + }, + "required": [ + "body", + "id" + ] + } + }, + { + "name": "linear_delete_comment", + "description": "Delete a comment", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Comment UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_issue_relations", + "description": "List relations for an issue (blocks, blocked by, related, duplicate)", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_issue_relation", + "description": "Create a relation between two issues", + "inputSchema": { + "type": "object", + "properties": { + "issue_id": { + "type": "string", + "description": "Source issue identifier or UUID" + }, + "related_issue_id": { + "type": "string", + "description": "Related issue identifier or UUID" + }, + "type": { + "type": "string", + "description": "Relation type: blocks, blocked_by, related, duplicate" + } + }, + "required": [ + "issue_id", + "related_issue_id", + "type" + ] + } + }, + { + "name": "linear_delete_issue_relation", + "description": "Delete an issue relation", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Relation UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_issue_labels", + "description": "List labels on a specific issue", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_attachments", + "description": "List attachments on an issue", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 25)" + }, + "issue_id": { + "type": "string", + "description": "Issue identifier or UUID" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "linear_create_attachment", + "description": "Create an attachment (link) on an issue", + "inputSchema": { + "type": "object", + "properties": { + "issue_id": { + "type": "string", + "description": "Issue identifier or UUID" + }, + "subtitle": { + "type": "string", + "description": "Subtitle" + }, + "title": { + "type": "string", + "description": "Attachment title" + }, + "url": { + "type": "string", + "description": "Attachment URL" + } + }, + "required": [ + "issue_id", + "url" + ] + } + }, + { + "name": "linear_list_projects", + "description": "List projects with optional filters. Start here for project status queries.", + "inputSchema": { + "type": "object", + "properties": { + "after": { + "type": "string", + "description": "Pagination cursor" + }, + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "state": { + "type": "string", + "description": "Filter by state: planned, started, paused, completed, canceled" + }, + "team": { + "type": "string", + "description": "Filter by team name or key" + } + } + } + }, + { + "name": "linear_search_projects", + "description": "Find Linear projects by name or keyword. Returns project IDs needed by get_project and list_project_updates. Start here when you know the project name.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 10)" + }, + "query": { + "type": "string", + "description": "Project name or keyword" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "linear_get_project", + "description": "Get a specific project with full detail including progress, members, recent status updates, and milestones. Accepts project UUID or slug. Use search_projects to find the ID first.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Project UUID or slug" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_project", + "description": "Create a project", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description (markdown)" + }, + "lead": { + "type": "string", + "description": "Project lead name or email" + }, + "name": { + "type": "string", + "description": "Project name" + }, + "start_date": { + "type": "string", + "description": "Start date (YYYY-MM-DD)" + }, + "state": { + "type": "string", + "description": "State: planned, started, paused, completed, canceled" + }, + "target_date": { + "type": "string", + "description": "Target date (YYYY-MM-DD)" + }, + "team": { + "type": "string", + "description": "Team name or key" + } + }, + "required": [ + "name", + "team" + ] + } + }, + { + "name": "linear_update_project", + "description": "Update a project", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "id": { + "type": "string", + "description": "Project UUID" + }, + "lead": { + "type": "string", + "description": "Project lead name or email" + }, + "name": { + "type": "string", + "description": "New name" + }, + "start_date": { + "type": "string", + "description": "Start date" + }, + "state": { + "type": "string", + "description": "State: planned, started, paused, completed, canceled" + }, + "target_date": { + "type": "string", + "description": "Target date" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_archive_project", + "description": "Archive a project", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Project UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_project_updates", + "description": "List status updates (health reports) for a project. Requires project UUID — use search_projects to find it. For a quick summary, get_project already includes the 5 most recent updates.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 10)" + }, + "project_id": { + "type": "string", + "description": "Project UUID" + } + }, + "required": [ + "project_id" + ] + } + }, + { + "name": "linear_create_project_update", + "description": "Create a project status update", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Update body (markdown)" + }, + "health": { + "type": "string", + "description": "Health: onTrack, atRisk, offTrack" + }, + "project_id": { + "type": "string", + "description": "Project UUID" + } + }, + "required": [ + "body", + "project_id" + ] + } + }, + { + "name": "linear_list_project_milestones", + "description": "List milestones for a project", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "project_id": { + "type": "string", + "description": "Project UUID" + } + }, + "required": [ + "project_id" + ] + } + }, + { + "name": "linear_create_project_milestone", + "description": "Create a project milestone", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description" + }, + "name": { + "type": "string", + "description": "Milestone name" + }, + "project_id": { + "type": "string", + "description": "Project UUID" + }, + "sort_order": { + "type": "string", + "description": "Sort order (number)" + }, + "target_date": { + "type": "string", + "description": "Target date (YYYY-MM-DD)" + } + }, + "required": [ + "name", + "project_id" + ] + } + }, + { + "name": "linear_list_cycles", + "description": "List cycles (sprints) with optional filters. Use to find the current sprint, then get_cycle for its issues.", + "inputSchema": { + "type": "object", + "properties": { + "after": { + "type": "string", + "description": "Pagination cursor" + }, + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "team": { + "type": "string", + "description": "Filter by team name or key" + } + } + } + }, + { + "name": "linear_get_cycle", + "description": "Get a specific cycle with its issues. Use after list_cycles to drill into a sprint.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Cycle UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_cycle", + "description": "Create a cycle", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description" + }, + "ends_at": { + "type": "string", + "description": "End date (ISO 8601)" + }, + "name": { + "type": "string", + "description": "Cycle name" + }, + "starts_at": { + "type": "string", + "description": "Start date (ISO 8601)" + }, + "team": { + "type": "string", + "description": "Team name or key" + } + }, + "required": [ + "ends_at", + "starts_at", + "team" + ] + } + }, + { + "name": "linear_update_cycle", + "description": "Update a cycle", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "ends_at": { + "type": "string", + "description": "End date" + }, + "id": { + "type": "string", + "description": "Cycle UUID" + }, + "name": { + "type": "string", + "description": "New name" + }, + "starts_at": { + "type": "string", + "description": "Start date" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_teams", + "description": "List all teams in the workspace. Use to discover team IDs needed by create_issue and list_workflow_states.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + } + } + } + }, + { + "name": "linear_get_team", + "description": "Get a specific team with members and settings. Accepts team UUID, name, or key.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Team UUID, name, or key" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_viewer", + "description": "Get the currently authenticated user. Use to find your user ID for filtering assigned issues via list_issues.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_list_users", + "description": "List users in the workspace. Use to find user IDs for assignee filtering.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + } + } + } + }, + { + "name": "linear_get_user", + "description": "Get a specific user with assigned issues count. Accepts user UUID, display name, or email.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User UUID, display name, or email" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_labels", + "description": "List issue labels in the workspace. Use to discover valid label names for list_issues filtering or create_issue.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 100)" + }, + "team": { + "type": "string", + "description": "Filter by team name or key" + } + } + } + }, + { + "name": "linear_create_label", + "description": "Create an issue label", + "inputSchema": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Hex color (e.g., #ff0000)" + }, + "description": { + "type": "string", + "description": "Description" + }, + "name": { + "type": "string", + "description": "Label name" + }, + "team": { + "type": "string", + "description": "Team name or key (omit for workspace label)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "linear_update_label", + "description": "Update an issue label", + "inputSchema": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "New hex color" + }, + "description": { + "type": "string", + "description": "New description" + }, + "id": { + "type": "string", + "description": "Label UUID" + }, + "name": { + "type": "string", + "description": "New name" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_delete_label", + "description": "Delete an issue label", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Label UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_workflow_states", + "description": "List workflow states for a team. Use before list_issues, create_issue, or update_issue to discover valid state names.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "team": { + "type": "string", + "description": "Team name or key" + } + } + } + }, + { + "name": "linear_create_workflow_state", + "description": "Create a workflow state", + "inputSchema": { + "type": "object", + "properties": { + "color": { + "type": "string", + "description": "Hex color" + }, + "description": { + "type": "string", + "description": "Description" + }, + "name": { + "type": "string", + "description": "State name" + }, + "position": { + "type": "string", + "description": "Sort position (number)" + }, + "team": { + "type": "string", + "description": "Team name or key" + }, + "type": { + "type": "string", + "description": "Type: triage, backlog, unstarted, started, completed, canceled" + } + }, + "required": [ + "color", + "name", + "team", + "type" + ] + } + }, + { + "name": "linear_list_documents", + "description": "List documents in the workspace. Filter by project to find project-specific docs.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + }, + "project": { + "type": "string", + "description": "Filter by project name" + } + } + } + }, + { + "name": "linear_search_documents", + "description": "Full-text search documents by keyword. For browsing by project, use list_documents with project filter.", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 25)" + }, + "query": { + "type": "string", + "description": "Search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "linear_get_document", + "description": "Get a specific document by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Document UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_document", + "description": "Create a document", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Content (markdown)" + }, + "icon": { + "type": "string", + "description": "Icon emoji" + }, + "project": { + "type": "string", + "description": "Associated project name or UUID" + }, + "title": { + "type": "string", + "description": "Document title" + } + }, + "required": [ + "title" + ] + } + }, + { + "name": "linear_update_document", + "description": "Update a document", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "New content (markdown)" + }, + "icon": { + "type": "string", + "description": "New icon emoji" + }, + "id": { + "type": "string", + "description": "Document UUID" + }, + "title": { + "type": "string", + "description": "New title" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_initiatives", + "description": "List initiatives", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + } + } + } + }, + { + "name": "linear_get_initiative", + "description": "Get a specific initiative by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Initiative UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_create_initiative", + "description": "Create an initiative", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description (markdown)" + }, + "name": { + "type": "string", + "description": "Initiative name" + }, + "status": { + "type": "string", + "description": "Status: Planned, Active, Completed" + }, + "target_date": { + "type": "string", + "description": "Target date (YYYY-MM-DD)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "linear_update_initiative", + "description": "Update an initiative", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New description" + }, + "id": { + "type": "string", + "description": "Initiative UUID" + }, + "name": { + "type": "string", + "description": "New name" + }, + "status": { + "type": "string", + "description": "Status: Planned, Active, Completed" + }, + "target_date": { + "type": "string", + "description": "Target date" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_favorites", + "description": "List favorites for the authenticated user", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_create_favorite", + "description": "Add an item to favorites", + "inputSchema": { + "type": "object", + "properties": { + "custom_view_id": { + "type": "string", + "description": "Custom view UUID" + }, + "cycle_id": { + "type": "string", + "description": "Cycle UUID" + }, + "issue_id": { + "type": "string", + "description": "Issue UUID" + }, + "project_id": { + "type": "string", + "description": "Project UUID" + } + } + } + }, + { + "name": "linear_delete_favorite", + "description": "Remove a favorite", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Favorite UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_webhooks", + "description": "List webhooks in the workspace", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_create_webhook", + "description": "Create a webhook", + "inputSchema": { + "type": "object", + "properties": { + "all_public_teams": { + "type": "string", + "description": "Subscribe to all public teams (true/false)" + }, + "label": { + "type": "string", + "description": "Label to filter events" + }, + "resource_types": { + "type": "string", + "description": "Comma-separated resource types: Issue, Comment, Project, Cycle, IssueLabel, etc." + }, + "team": { + "type": "string", + "description": "Team name or key (omit for all teams)" + }, + "url": { + "type": "string", + "description": "Webhook URL" + } + }, + "required": [ + "url" + ] + } + }, + { + "name": "linear_delete_webhook", + "description": "Delete a webhook", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Webhook UUID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "linear_list_notifications", + "description": "List notifications for the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + } + } + } + }, + { + "name": "linear_list_templates", + "description": "List issue templates", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_get_organization", + "description": "Get the current organization details", + "inputSchema": { + "type": "object" + } + }, + { + "name": "linear_list_custom_views", + "description": "List custom views (saved filters)", + "inputSchema": { + "type": "object", + "properties": { + "first": { + "type": "string", + "description": "Max results (default 50)" + } + } + } + }, + { + "name": "linear_create_custom_view", + "description": "Create a custom view (saved filter)", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Description" + }, + "filter_assignee": { + "type": "string", + "description": "Filter by assignee (name or 'me')" + }, + "filter_label": { + "type": "string", + "description": "Filter by label name" + }, + "filter_priority": { + "type": "string", + "description": "Filter by priority (1-4)" + }, + "filter_state": { + "type": "string", + "description": "Filter by state names (comma-separated)" + }, + "name": { + "type": "string", + "description": "View name" + }, + "team": { + "type": "string", + "description": "Team name or key" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "linear_rate_limit", + "description": "Get current API rate limit status", + "inputSchema": { + "type": "object" + } + }, + { + "name": "sentry_get_organization", + "description": "Get details of the Sentry organization", + "inputSchema": { + "type": "object" + } + }, + { + "name": "sentry_list_org_projects", + "description": "List all projects in the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + } + } + } + }, + { + "name": "sentry_list_org_teams", + "description": "List all teams in the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + } + } + } + }, + { + "name": "sentry_list_org_members", + "description": "List members of the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + } + } + } + }, + { + "name": "sentry_get_org_member", + "description": "Get details of an organization member", + "inputSchema": { + "type": "object", + "properties": { + "member_id": { + "type": "string", + "description": "Member ID (or 'me' for current user)" + } + }, + "required": [ + "member_id" + ] + } + }, + { + "name": "sentry_list_org_repos", + "description": "List repositories connected to the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + } + } + } + }, + { + "name": "sentry_resolve_short_id", + "description": "Resolve a Sentry short ID (e.g., PROJECT-123) to full error issue details. Use to look up a bug by its short reference.", + "inputSchema": { + "type": "object", + "properties": { + "short_id": { + "type": "string", + "description": "Short ID (e.g., PROJECT-123)" + } + }, + "required": [ + "short_id" + ] + } + }, + { + "name": "sentry_list_projects", + "description": "List all projects accessible to the auth token", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + } + } + } + }, + { + "name": "sentry_get_project", + "description": "Get details of a specific project", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_update_project", + "description": "Update a project's settings", + "inputSchema": { + "type": "object", + "properties": { + "isBookmarked": { + "type": "string", + "description": "Bookmark project (true/false)" + }, + "name": { + "type": "string", + "description": "New name" + }, + "platform": { + "type": "string", + "description": "Platform (e.g., python, javascript)" + }, + "project": { + "type": "string", + "description": "Project slug" + }, + "slug": { + "type": "string", + "description": "New slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_delete_project", + "description": "Delete a project", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_create_project", + "description": "Create a new project under a team", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Project name" + }, + "platform": { + "type": "string", + "description": "Platform (e.g., python, javascript)" + }, + "slug": { + "type": "string", + "description": "Project slug (optional, auto-generated from name)" + }, + "team": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "name", + "team" + ] + } + }, + { + "name": "sentry_list_project_keys", + "description": "List a project's client keys (DSN)", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_list_project_envs", + "description": "List a project's environments", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_list_project_tags", + "description": "List tags for a project", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_get_project_stats", + "description": "Get error and crash event count statistics for a project. Use to monitor error rate and volume.", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + }, + "resolution": { + "type": "string", + "description": "Resolution in seconds (e.g., 3600 for hourly)" + }, + "since": { + "type": "string", + "description": "Unix timestamp for start" + }, + "stat": { + "type": "string", + "description": "Stat type: received, rejected, blacklisted, generated (default: received)" + }, + "until": { + "type": "string", + "description": "Unix timestamp for end" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_list_project_hooks", + "description": "List service hooks for a project", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_get_team", + "description": "Get details of a specific team", + "inputSchema": { + "type": "object", + "properties": { + "team": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "team" + ] + } + }, + { + "name": "sentry_create_team", + "description": "Create a new team in the organization", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Team name" + }, + "slug": { + "type": "string", + "description": "Team slug (optional)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "sentry_delete_team", + "description": "Delete a team", + "inputSchema": { + "type": "object", + "properties": { + "team": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "team" + ] + } + }, + { + "name": "sentry_list_team_projects", + "description": "List projects belonging to a team", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "team": { + "type": "string", + "description": "Team slug" + } + }, + "required": [ + "team" + ] + } + }, + { + "name": "sentry_list_issues", + "description": "List errors and exceptions for a project. Start here for error tracking, debugging, and finding unresolved bugs or crashes.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "project": { + "type": "string", + "description": "Project slug" + }, + "query": { + "type": "string", + "description": "Search query (e.g., 'is:unresolved', 'assigned:me')" + }, + "sort": { + "type": "string", + "description": "Sort: date, new, freq, user (default: date)" + }, + "statsPeriod": { + "type": "string", + "description": "Stats period: '' (default), '24h', or '14d'" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_get_issue", + "description": "Get details of a specific error or exception issue, including stacktrace and debugging context", + "inputSchema": { + "type": "object", + "properties": { + "issue_id": { + "type": "string", + "description": "Issue ID" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "sentry_update_issue", + "description": "Update an error issue (resolve, assign, triage, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "assignedTo": { + "type": "string", + "description": "Assign to user (email or username, empty to unassign)" + }, + "hasSeen": { + "type": "string", + "description": "Mark as seen (true/false)" + }, + "isBookmarked": { + "type": "string", + "description": "Bookmark (true/false)" + }, + "isPublic": { + "type": "string", + "description": "Make public (true/false)" + }, + "isSubscribed": { + "type": "string", + "description": "Subscribe (true/false)" + }, + "issue_id": { + "type": "string", + "description": "Issue ID" + }, + "status": { + "type": "string", + "description": "Status: resolved, unresolved, ignored" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "sentry_delete_issue", + "description": "Delete an issue", + "inputSchema": { + "type": "object", + "properties": { + "issue_id": { + "type": "string", + "description": "Issue ID" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "sentry_list_issue_events", + "description": "List error occurrences and crash events for a specific issue", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "issue_id": { + "type": "string", + "description": "Issue ID" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "sentry_list_issue_hashes", + "description": "List hashes (fingerprints) for an issue", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "issue_id": { + "type": "string", + "description": "Issue ID" + } + }, + "required": [ + "issue_id" + ] + } + }, + { + "name": "sentry_get_issue_tag_values", + "description": "Get tag value distribution for an issue", + "inputSchema": { + "type": "object", + "properties": { + "issue_id": { + "type": "string", + "description": "Issue ID" + }, + "tag_name": { + "type": "string", + "description": "Tag key (e.g., browser, os, url, environment)" + } + }, + "required": [ + "issue_id", + "tag_name" + ] + } + }, + { + "name": "sentry_list_project_events", + "description": "List error and exception events for a project. Use to investigate crashes and debug production issues.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_get_event", + "description": "Get details of a specific event", + "inputSchema": { + "type": "object", + "properties": { + "event_id": { + "type": "string", + "description": "Event ID" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "event_id", + "project" + ] + } + }, + { + "name": "sentry_list_org_issues", + "description": "List error and exception issues across the entire organization. Search all projects for bugs, crashes, and unresolved problems.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "project": { + "type": "string", + "description": "Filter by project slug" + }, + "query": { + "type": "string", + "description": "Search query (e.g., 'is:unresolved level:error')" + }, + "sort": { + "type": "string", + "description": "Sort: date, new, freq, user" + }, + "statsPeriod": { + "type": "string", + "description": "Stats period: '' (default), '24h', or '14d'" + } + } + } + }, + { + "name": "sentry_list_releases", + "description": "List releases for the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "project": { + "type": "string", + "description": "Filter by project slug" + }, + "query": { + "type": "string", + "description": "Filter releases by version" + } + } + } + }, + { + "name": "sentry_get_release", + "description": "Get details of a specific release", + "inputSchema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Release version identifier" + } + }, + "required": [ + "version" + ] + } + }, + { + "name": "sentry_create_release", + "description": "Create a new release", + "inputSchema": { + "type": "object", + "properties": { + "dateReleased": { + "type": "string", + "description": "Release date (ISO 8601)" + }, + "projects": { + "type": "string", + "description": "Comma-separated project slugs" + }, + "ref": { + "type": "string", + "description": "Git ref (commit SHA or tag)" + }, + "url": { + "type": "string", + "description": "URL for the release" + }, + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "projects", + "version" + ] + } + }, + { + "name": "sentry_delete_release", + "description": "Delete a release", + "inputSchema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "version" + ] + } + }, + { + "name": "sentry_list_release_commits", + "description": "List commits in a release", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "version" + ] + } + }, + { + "name": "sentry_list_release_deploys", + "description": "List deploys for a release", + "inputSchema": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "version" + ] + } + }, + { + "name": "sentry_create_deploy", + "description": "Create a deploy for a release", + "inputSchema": { + "type": "object", + "properties": { + "dateFinished": { + "type": "string", + "description": "Finish time (ISO 8601)" + }, + "dateStarted": { + "type": "string", + "description": "Start time (ISO 8601)" + }, + "environment": { + "type": "string", + "description": "Environment name (e.g., production)" + }, + "name": { + "type": "string", + "description": "Deploy name" + }, + "url": { + "type": "string", + "description": "Deploy URL" + }, + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "environment", + "version" + ] + } + }, + { + "name": "sentry_list_release_files", + "description": "List files (artifacts) in a release", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "version": { + "type": "string", + "description": "Release version" + } + }, + "required": [ + "version" + ] + } + }, + { + "name": "sentry_list_metric_alerts", + "description": "List metric alert rules for monitoring thresholds and warnings across the organization", + "inputSchema": { + "type": "object" + } + }, + { + "name": "sentry_get_metric_alert", + "description": "Get a specific metric alert rule", + "inputSchema": { + "type": "object", + "properties": { + "alert_rule_id": { + "type": "string", + "description": "Alert rule ID" + } + }, + "required": [ + "alert_rule_id" + ] + } + }, + { + "name": "sentry_delete_metric_alert", + "description": "Delete a metric alert rule", + "inputSchema": { + "type": "object", + "properties": { + "alert_rule_id": { + "type": "string", + "description": "Alert rule ID" + } + }, + "required": [ + "alert_rule_id" + ] + } + }, + { + "name": "sentry_list_issue_alerts", + "description": "List error alert rules that trigger notifications for a project", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "project" + ] + } + }, + { + "name": "sentry_get_issue_alert", + "description": "Get a specific issue alert rule", + "inputSchema": { + "type": "object", + "properties": { + "alert_rule_id": { + "type": "string", + "description": "Alert rule ID" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "alert_rule_id", + "project" + ] + } + }, + { + "name": "sentry_delete_issue_alert", + "description": "Delete an issue alert rule", + "inputSchema": { + "type": "object", + "properties": { + "alert_rule_id": { + "type": "string", + "description": "Alert rule ID" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "alert_rule_id", + "project" + ] + } + }, + { + "name": "sentry_list_monitors", + "description": "List cron monitors for the organization", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "project": { + "type": "string", + "description": "Filter by project slug" + } + } + } + }, + { + "name": "sentry_get_monitor", + "description": "Get a specific cron monitor", + "inputSchema": { + "type": "object", + "properties": { + "monitor_id": { + "type": "string", + "description": "Monitor ID or slug" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "monitor_id", + "project" + ] + } + }, + { + "name": "sentry_delete_monitor", + "description": "Delete a cron monitor", + "inputSchema": { + "type": "object", + "properties": { + "monitor_id": { + "type": "string", + "description": "Monitor ID or slug" + }, + "project": { + "type": "string", + "description": "Project slug" + } + }, + "required": [ + "monitor_id", + "project" + ] + } + }, + { + "name": "sentry_list_saved_queries", + "description": "List saved Discover queries", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "sortBy": { + "type": "string", + "description": "Sort by: dateCreated, dateUpdated, name, myqueries (default: dateUpdated)" + } + } + } + }, + { + "name": "sentry_get_saved_query", + "description": "Get a specific saved Discover query", + "inputSchema": { + "type": "object", + "properties": { + "query_id": { + "type": "string", + "description": "Saved query ID" + } + }, + "required": [ + "query_id" + ] + } + }, + { + "name": "sentry_delete_saved_query", + "description": "Delete a saved Discover query", + "inputSchema": { + "type": "object", + "properties": { + "query_id": { + "type": "string", + "description": "Saved query ID" + } + }, + "required": [ + "query_id" + ] + } + }, + { + "name": "sentry_list_replays", + "description": "List session replay recordings. Use to visually reproduce and investigate specific user sessions.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "limit": { + "type": "string", + "description": "Max results (default 50)" + }, + "query": { + "type": "string", + "description": "Search query" + }, + "statsPeriod": { + "type": "string", + "description": "Stats period: '' (default), '24h', or '14d'" + } + } + } + }, + { + "name": "sentry_get_replay", + "description": "Get details of a specific replay", + "inputSchema": { + "type": "object", + "properties": { + "replay_id": { + "type": "string", + "description": "Replay ID" + } + }, + "required": [ + "replay_id" + ] + } + }, + { + "name": "sentry_delete_replay", + "description": "Delete a replay", + "inputSchema": { + "type": "object", + "properties": { + "replay_id": { + "type": "string", + "description": "Replay ID" + } + }, + "required": [ + "replay_id" + ] + } + }, + { + "name": "slack_token_status", + "description": "Check token health for all workspaces: type (OAuth vs browser session), age, auto-refresh status, and source.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "slack_refresh_tokens", + "description": "Force refresh browser session tokens. Tries cookie-based HTTP refresh first (works on all platforms), then Chrome LevelDB extraction (macOS). OAuth tokens (xoxp-) don't need refresh.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_list_workspaces", + "description": "List all configured Slack workspaces with team IDs and names. Use to find the team_id for a specific workspace.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "slack_list_conversations", + "description": "Start here to discover channels. List channels and DMs in the workspace. Filter by type (public_channel, private_channel, im, mpim). Returns channel IDs needed by most other Slack tools.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "exclude_archived": { + "type": "string", + "description": "Exclude archived channels (default true)" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 100, max 1000)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "types": { + "type": "string", + "description": "Comma-separated types: public_channel, private_channel, im, mpim (default: public_channel,private_channel)" + } + } + } + }, + { + "name": "slack_get_conversation_info", + "description": "Get detailed information about a specific channel or DM, including topic, purpose, and member count. Use after list_conversations to inspect a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel or DM ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_conversations_history", + "description": "Start here to read channel messages. Returns messages in reverse chronological order. Requires channel ID (C...), not channel name. Use list_conversations to find IDs.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel or DM ID" + }, + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "latest": { + "type": "string", + "description": "Unix timestamp — get messages before this time" + }, + "limit": { + "type": "string", + "description": "Number of messages to fetch (default 50, max 100)" + }, + "oldest": { + "type": "string", + "description": "Unix timestamp — get messages after this time" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_get_thread", + "description": "Get all replies in a message thread. Use after conversations_history to read full thread replies. Requires channel ID and thread_ts from conversations_history.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel or DM ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "thread_ts": { + "type": "string", + "description": "Thread parent message timestamp" + } + }, + "required": [ + "channel_id", + "thread_ts" + ] + } + }, + { + "name": "slack_create_conversation", + "description": "Create a new channel.", + "inputSchema": { + "type": "object", + "properties": { + "is_private": { + "type": "string", + "description": "Create as private channel (default false)" + }, + "name": { + "type": "string", + "description": "Channel name (lowercase, no spaces, max 80 chars)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "slack_archive_conversation", + "description": "Archive a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID to archive" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_invite_to_conversation", + "description": "Invite users to a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "user_ids": { + "type": "string", + "description": "Comma-separated user IDs to invite" + } + }, + "required": [ + "channel_id", + "user_ids" + ] + } + }, + { + "name": "slack_kick_from_conversation", + "description": "Remove a user from a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "user_id": { + "type": "string", + "description": "User ID to remove" + } + }, + "required": [ + "channel_id", + "user_id" + ] + } + }, + { + "name": "slack_set_conversation_topic", + "description": "Set the topic of a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "topic": { + "type": "string", + "description": "New topic text" + } + }, + "required": [ + "channel_id", + "topic" + ] + } + }, + { + "name": "slack_set_conversation_purpose", + "description": "Set the purpose of a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "purpose": { + "type": "string", + "description": "New purpose text" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id", + "purpose" + ] + } + }, + { + "name": "slack_join_conversation", + "description": "Join a public channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID to join" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_leave_conversation", + "description": "Leave a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID to leave" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_rename_conversation", + "description": "Rename a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "name": { + "type": "string", + "description": "New channel name" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id", + "name" + ] + } + }, + { + "name": "slack_send_message", + "description": "Send (post) a message to a channel or DM. Requires channel ID (C...), not channel name. Supports Slack mrkdwn formatting and threads.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel or DM ID to send to" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "text": { + "type": "string", + "description": "Message text (supports Slack mrkdwn)" + }, + "thread_ts": { + "type": "string", + "description": "Thread timestamp to reply in a thread (optional)" + } + }, + "required": [ + "channel_id", + "text" + ] + } + }, + { + "name": "slack_update_message", + "description": "Update an existing message.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID where the message is" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "text": { + "type": "string", + "description": "New message text" + }, + "ts": { + "type": "string", + "description": "Timestamp of the message to update" + } + }, + "required": [ + "channel_id", + "text", + "ts" + ] + } + }, + { + "name": "slack_delete_message", + "description": "Delete a message from a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Timestamp of the message to delete" + } + }, + "required": [ + "channel_id", + "ts" + ] + } + }, + { + "name": "slack_search_messages", + "description": "Start here to find messages. Search across the entire Slack workspace. Supports Slack search syntax: from:@user, in:#channel, has:link, before:2024-01-01, after:2024-01-01.", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "description": "Number of results (default 20, max 100)" + }, + "query": { + "type": "string", + "description": "Search query (supports Slack search modifiers)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "slack_add_reaction", + "description": "Add an emoji reaction to a message.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "emoji": { + "type": "string", + "description": "Emoji name without colons (e.g., thumbsup)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Message timestamp" + } + }, + "required": [ + "channel_id", + "emoji", + "ts" + ] + } + }, + { + "name": "slack_remove_reaction", + "description": "Remove an emoji reaction from a message.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "emoji": { + "type": "string", + "description": "Emoji name without colons" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Message timestamp" + } + }, + "required": [ + "channel_id", + "emoji", + "ts" + ] + } + }, + { + "name": "slack_get_reactions", + "description": "Get all reactions on a message.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Message timestamp" + } + }, + "required": [ + "channel_id", + "ts" + ] + } + }, + { + "name": "slack_add_pin", + "description": "Pin a message in a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Message timestamp to pin" + } + }, + "required": [ + "channel_id", + "ts" + ] + } + }, + { + "name": "slack_remove_pin", + "description": "Remove a pinned message.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "ts": { + "type": "string", + "description": "Message timestamp to unpin" + } + }, + "required": [ + "channel_id", + "ts" + ] + } + }, + { + "name": "slack_list_pins", + "description": "List all pinned items in a channel. Use to find important messages and references pinned by the team.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_schedule_message", + "description": "Schedule a message to be sent at a specific time.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "post_at": { + "type": "string", + "description": "Unix timestamp for when to send the message" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "text": { + "type": "string", + "description": "Message text" + }, + "thread_ts": { + "type": "string", + "description": "Thread timestamp to reply in a thread (optional)" + } + }, + "required": [ + "channel_id", + "post_at", + "text" + ] + } + }, + { + "name": "slack_list_users", + "description": "Start here to find users. List all users in the workspace with display names, emails, and IDs. Supports pagination for large workspaces.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "limit": { + "type": "string", + "description": "Max users per page (default 200, max 1000)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_get_user_info", + "description": "Get detailed profile for a single user including status, timezone, and admin status. Use after list_users when you need full details for a specific person.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "user_id": { + "type": "string", + "description": "Slack user ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "slack_get_user_presence", + "description": "Get a user's current presence status (active/away). Use after list_users to check if someone is online.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "user_id": { + "type": "string", + "description": "Slack user ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "slack_list_user_groups", + "description": "List all user groups (handles like @engineering) in the workspace. Use to find group IDs and membership.", + "inputSchema": { + "type": "object", + "properties": { + "include_disabled": { + "type": "string", + "description": "Include disabled user groups (default false)" + }, + "include_users": { + "type": "string", + "description": "Include list of member user IDs (default false)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_get_user_group", + "description": "Get members of a specific user group.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "usergroup_id": { + "type": "string", + "description": "User group ID" + } + }, + "required": [ + "usergroup_id" + ] + } + }, + { + "name": "slack_auth_test", + "description": "Test authentication and get current user/workspace info. Use to verify credentials and find your own user ID.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_team_info", + "description": "Get information about the workspace/team.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_upload_file", + "description": "Upload a text file or snippet to a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channels": { + "type": "string", + "description": "Comma-separated channel IDs to share the file in" + }, + "content": { + "type": "string", + "description": "Text content of the file" + }, + "filename": { + "type": "string", + "description": "Filename (e.g., report.txt)" + }, + "filetype": { + "type": "string", + "description": "File type identifier (e.g., text, python, javascript)" + }, + "initial_comment": { + "type": "string", + "description": "Message to include with the file" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "thread_ts": { + "type": "string", + "description": "Thread timestamp to upload into a thread (optional)" + }, + "title": { + "type": "string", + "description": "Title for the file" + } + }, + "required": [ + "channels", + "content", + "filename" + ] + } + }, + { + "name": "slack_list_files", + "description": "List files shared in the workspace. Use to find documents, images, and snippets. Filter by channel, user, or type.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Filter by channel ID (optional)" + }, + "count": { + "type": "string", + "description": "Number of files to return (default 20, max 100)" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "types": { + "type": "string", + "description": "Filter by file type: spaces, snippets, images, gdocs, zips, pdfs (optional)" + }, + "user_id": { + "type": "string", + "description": "Filter by user ID (optional)" + } + } + } + }, + { + "name": "slack_delete_file", + "description": "Delete a file.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID to delete" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "slack_list_emoji", + "description": "List all custom emoji in the workspace.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_set_status", + "description": "Set the authenticated user's status.", + "inputSchema": { + "type": "object", + "properties": { + "status_emoji": { + "type": "string", + "description": "Status emoji (e.g., :house_with_garden:)" + }, + "status_expiration": { + "type": "string", + "description": "Unix timestamp when status expires (0 for no expiration)" + }, + "status_text": { + "type": "string", + "description": "Status text" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "status_text" + ] + } + }, + { + "name": "slack_list_bookmarks", + "description": "List bookmarks in a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "channel_id" + ] + } + }, + { + "name": "slack_add_bookmark", + "description": "Add a bookmark to a channel.", + "inputSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "emoji": { + "type": "string", + "description": "Emoji for the bookmark (optional)" + }, + "link": { + "type": "string", + "description": "URL to bookmark" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "title": { + "type": "string", + "description": "Bookmark title" + } + }, + "required": [ + "channel_id", + "link", + "title" + ] + } + }, + { + "name": "slack_remove_bookmark", + "description": "Remove a bookmark from a channel.", + "inputSchema": { + "type": "object", + "properties": { + "bookmark_id": { + "type": "string", + "description": "Bookmark ID to remove" + }, + "channel_id": { + "type": "string", + "description": "Channel ID" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "bookmark_id", + "channel_id" + ] + } + }, + { + "name": "slack_add_reminder", + "description": "Create a reminder. Time can be natural language (e.g., 'in 15 minutes', 'tomorrow at 9am').", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + }, + "text": { + "type": "string", + "description": "Reminder text" + }, + "time": { + "type": "string", + "description": "When to remind (natural language or Unix timestamp)" + }, + "user": { + "type": "string", + "description": "User ID to remind (defaults to authenticated user)" + } + }, + "required": [ + "text", + "time" + ] + } + }, + { + "name": "slack_list_reminders", + "description": "List all reminders for the authenticated user.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + } + } + }, + { + "name": "slack_delete_reminder", + "description": "Delete a reminder.", + "inputSchema": { + "type": "object", + "properties": { + "reminder_id": { + "type": "string", + "description": "Reminder ID to delete" + }, + "team_id": { + "type": "string", + "description": "Workspace team ID (omit to use default workspace). Use slack_list_workspaces to see available workspaces." + } + }, + "required": [ + "reminder_id" + ] + } + }, + { + "name": "metabase_list_databases", + "description": "List all databases configured in Metabase", + "inputSchema": { + "type": "object" + } + }, + { + "name": "metabase_get_database", + "description": "Get details of a specific database including its tables", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "metabase_list_tables", + "description": "List all tables in a specific database with metadata", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "metabase_get_table", + "description": "Get detailed metadata for a specific table", + "inputSchema": { + "type": "object", + "properties": { + "table_id": { + "type": "string", + "description": "Table ID" + } + }, + "required": [ + "table_id" + ] + } + }, + { + "name": "metabase_get_table_fields", + "description": "Get all fields/columns for a specific table with types and metadata", + "inputSchema": { + "type": "object", + "properties": { + "table_id": { + "type": "string", + "description": "Table ID" + } + }, + "required": [ + "table_id" + ] + } + }, + { + "name": "metabase_execute_query", + "description": "Execute a native SQL analytics query against a Metabase-connected database and return results as JSON. Use for ad-hoc analytics, BI reporting, and data exploration.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID to query" + }, + "query": { + "type": "string", + "description": "SQL query string" + } + }, + "required": [ + "database_id", + "query" + ] + } + }, + { + "name": "metabase_execute_card", + "description": "Execute a saved question/card and return its results", + "inputSchema": { + "type": "object", + "properties": { + "card_id": { + "type": "string", + "description": "Card/question ID" + }, + "parameters": { + "type": "string", + "description": "Optional JSON array of parameter objects [{type, target, value}]" + } + }, + "required": [ + "card_id" + ] + } + }, + { + "name": "metabase_list_cards", + "description": "List all saved questions/cards. Optionally filter by type.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter: all (default), mine, bookmarked, archived" + } + } + } + }, + { + "name": "metabase_get_card", + "description": "Get details of a specific saved question/card", + "inputSchema": { + "type": "object", + "properties": { + "card_id": { + "type": "string", + "description": "Card ID" + } + }, + "required": [ + "card_id" + ] + } + }, + { + "name": "metabase_create_card", + "description": "Create a new saved question/card with a native SQL query", + "inputSchema": { + "type": "object", + "properties": { + "collection_id": { + "type": "string", + "description": "Optional collection ID to save into" + }, + "database_id": { + "type": "string", + "description": "Database ID" + }, + "description": { + "type": "string", + "description": "Optional description" + }, + "display": { + "type": "string", + "description": "Visualization type: table, bar, line, pie, scalar, etc. (default: table)" + }, + "name": { + "type": "string", + "description": "Name of the question" + }, + "query": { + "type": "string", + "description": "SQL query string" + } + }, + "required": [ + "database_id", + "name", + "query" + ] + } + }, + { + "name": "metabase_update_card", + "description": "Update a saved question/card (name, description, query, visualization)", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Set to true to archive the card" + }, + "card_id": { + "type": "string", + "description": "Card ID" + }, + "database_id": { + "type": "string", + "description": "Database ID (required if changing query)" + }, + "description": { + "type": "string", + "description": "New description" + }, + "display": { + "type": "string", + "description": "Visualization type: table, bar, line, pie, scalar, etc." + }, + "name": { + "type": "string", + "description": "New name" + }, + "query": { + "type": "string", + "description": "New SQL query" + } + }, + "required": [ + "card_id" + ] + } + }, + { + "name": "metabase_delete_card", + "description": "Delete (archive) a saved question/card", + "inputSchema": { + "type": "object", + "properties": { + "card_id": { + "type": "string", + "description": "Card ID" + } + }, + "required": [ + "card_id" + ] + } + }, + { + "name": "metabase_list_dashboards", + "description": "List all Metabase analytics dashboards for reporting and data visualization", + "inputSchema": { + "type": "object" + } + }, + { + "name": "metabase_get_dashboard", + "description": "Get details of a dashboard including its cards and layout", + "inputSchema": { + "type": "object", + "properties": { + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "metabase_create_dashboard", + "description": "Create a new dashboard", + "inputSchema": { + "type": "object", + "properties": { + "collection_id": { + "type": "string", + "description": "Optional collection ID" + }, + "description": { + "type": "string", + "description": "Optional description" + }, + "name": { + "type": "string", + "description": "Dashboard name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "metabase_update_dashboard", + "description": "Update a dashboard's name, description, or other properties", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Set to true to archive the dashboard" + }, + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "name": { + "type": "string", + "description": "New name" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "metabase_delete_dashboard", + "description": "Delete (archive) a dashboard", + "inputSchema": { + "type": "object", + "properties": { + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "metabase_add_card_to_dashboard", + "description": "Add a saved question/card to a dashboard", + "inputSchema": { + "type": "object", + "properties": { + "card_id": { + "type": "string", + "description": "Card ID to add" + }, + "col": { + "type": "string", + "description": "Column position (default: 0)" + }, + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + }, + "row": { + "type": "string", + "description": "Row position (default: 0)" + }, + "size_x": { + "type": "string", + "description": "Width in grid units (default: 6)" + }, + "size_y": { + "type": "string", + "description": "Height in grid units (default: 4)" + } + }, + "required": [ + "card_id", + "dashboard_id" + ] + } + }, + { + "name": "metabase_list_collections", + "description": "List all collections (folders for organizing questions and dashboards)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "metabase_get_collection", + "description": "Get details and items in a specific collection", + "inputSchema": { + "type": "object", + "properties": { + "collection_id": { + "type": "string", + "description": "Collection ID (use 'root' for the root collection)" + } + }, + "required": [ + "collection_id" + ] + } + }, + { + "name": "metabase_create_collection", + "description": "Create a new collection for organizing questions and dashboards", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Optional description" + }, + "name": { + "type": "string", + "description": "Collection name" + }, + "parent_id": { + "type": "string", + "description": "Optional parent collection ID for nesting" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "metabase_update_collection", + "description": "Update a collection's name, description, or parent", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Set to true to archive" + }, + "collection_id": { + "type": "string", + "description": "Collection ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "name": { + "type": "string", + "description": "New name" + }, + "parent_id": { + "type": "string", + "description": "New parent collection ID" + } + }, + "required": [ + "collection_id" + ] + } + }, + { + "name": "metabase_search", + "description": "Search across all Metabase content (questions, dashboards, collections, tables, databases). Start here for BI and reporting workflows.", + "inputSchema": { + "type": "object", + "properties": { + "models": { + "type": "string", + "description": "Comma-separated types to search: card, dashboard, collection, table, database" + }, + "query": { + "type": "string", + "description": "Search query string" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "aws_get_caller_identity", + "description": "Get details about the IAM identity making the API call", + "inputSchema": { + "type": "object" + } + }, + { + "name": "aws_s3_list_buckets", + "description": "List all S3 buckets in the account", + "inputSchema": { + "type": "object" + } + }, + { + "name": "aws_s3_list_objects", + "description": "List objects in an S3 bucket", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "continuation_token": { + "type": "string", + "description": "Token for pagination" + }, + "max_keys": { + "type": "string", + "description": "Maximum number of keys to return (default 1000)" + }, + "prefix": { + "type": "string", + "description": "Object key prefix filter" + } + }, + "required": [ + "bucket" + ] + } + }, + { + "name": "aws_s3_get_object", + "description": "Get an object from S3 (returns metadata and body as text for text types, base64 for binary)", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "key": { + "type": "string", + "description": "Object key" + } + }, + "required": [ + "bucket", + "key" + ] + } + }, + { + "name": "aws_s3_put_object", + "description": "Upload an object to S3", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Object content (text)" + }, + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "content_type": { + "type": "string", + "description": "MIME type (default: application/octet-stream)" + }, + "key": { + "type": "string", + "description": "Object key" + } + }, + "required": [ + "body", + "bucket", + "key" + ] + } + }, + { + "name": "aws_s3_delete_object", + "description": "Delete an object from S3", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "key": { + "type": "string", + "description": "Object key" + } + }, + "required": [ + "bucket", + "key" + ] + } + }, + { + "name": "aws_s3_head_object", + "description": "Get metadata for an S3 object without downloading it", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "key": { + "type": "string", + "description": "Object key" + } + }, + "required": [ + "bucket", + "key" + ] + } + }, + { + "name": "aws_s3_copy_object", + "description": "Copy an object within S3", + "inputSchema": { + "type": "object", + "properties": { + "dest_bucket": { + "type": "string", + "description": "Destination bucket name" + }, + "dest_key": { + "type": "string", + "description": "Destination object key" + }, + "source_bucket": { + "type": "string", + "description": "Source bucket name" + }, + "source_key": { + "type": "string", + "description": "Source object key" + } + }, + "required": [ + "dest_bucket", + "dest_key", + "source_bucket", + "source_key" + ] + } + }, + { + "name": "aws_ec2_describe_instances", + "description": "List EC2 instances (servers/VMs) with optional filters. Start here for infrastructure inventory and production servers.", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters [{\"Name\":\"tag:Env\",\"Values\":[\"prod\"]}]" + }, + "instance_ids": { + "type": "string", + "description": "Comma-separated instance IDs" + }, + "max_results": { + "type": "string", + "description": "Maximum number of results" + } + } + } + }, + { + "name": "aws_ec2_describe_instance", + "description": "Get details for a specific EC2 instance", + "inputSchema": { + "type": "object", + "properties": { + "instance_id": { + "type": "string", + "description": "Instance ID" + } + }, + "required": [ + "instance_id" + ] + } + }, + { + "name": "aws_ec2_start_instances", + "description": "Start one or more EC2 instances", + "inputSchema": { + "type": "object", + "properties": { + "instance_ids": { + "type": "string", + "description": "Comma-separated instance IDs to start" + } + }, + "required": [ + "instance_ids" + ] + } + }, + { + "name": "aws_ec2_stop_instances", + "description": "Stop one or more EC2 instances", + "inputSchema": { + "type": "object", + "properties": { + "instance_ids": { + "type": "string", + "description": "Comma-separated instance IDs to stop" + } + }, + "required": [ + "instance_ids" + ] + } + }, + { + "name": "aws_ec2_describe_security_groups", + "description": "List security groups with optional filters", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters" + }, + "group_ids": { + "type": "string", + "description": "Comma-separated security group IDs" + } + } + } + }, + { + "name": "aws_ec2_describe_vpcs", + "description": "List VPCs", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters" + }, + "vpc_ids": { + "type": "string", + "description": "Comma-separated VPC IDs" + } + } + } + }, + { + "name": "aws_ec2_describe_subnets", + "description": "List subnets", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters" + }, + "subnet_ids": { + "type": "string", + "description": "Comma-separated subnet IDs" + } + } + } + }, + { + "name": "aws_ec2_describe_images", + "description": "List AMI images", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters" + }, + "image_ids": { + "type": "string", + "description": "Comma-separated AMI IDs" + }, + "owners": { + "type": "string", + "description": "Comma-separated owner IDs or aliases (self, amazon)" + } + } + } + }, + { + "name": "aws_ec2_describe_volumes", + "description": "List EBS volumes", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "JSON array of filters" + }, + "volume_ids": { + "type": "string", + "description": "Comma-separated volume IDs" + } + } + } + }, + { + "name": "aws_ec2_describe_addresses", + "description": "List Elastic IP addresses", + "inputSchema": { + "type": "object", + "properties": { + "allocation_ids": { + "type": "string", + "description": "Comma-separated allocation IDs" + }, + "filters": { + "type": "string", + "description": "JSON array of filters" + } + } + } + }, + { + "name": "aws_ec2_describe_key_pairs", + "description": "List EC2 key pairs", + "inputSchema": { + "type": "object", + "properties": { + "key_names": { + "type": "string", + "description": "Comma-separated key pair names" + } + } + } + }, + { + "name": "aws_lambda_list_functions", + "description": "List Lambda functions", + "inputSchema": { + "type": "object", + "properties": { + "max_items": { + "type": "string", + "description": "Maximum number of functions to return" + } + } + } + }, + { + "name": "aws_lambda_get_function", + "description": "Get details about a Lambda function", + "inputSchema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Function name or ARN" + } + }, + "required": [ + "function_name" + ] + } + }, + { + "name": "aws_lambda_invoke", + "description": "Invoke (run/trigger) a Lambda function. Use after list_functions to find the function name.", + "inputSchema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Function name or ARN" + }, + "invocation_type": { + "type": "string", + "description": "RequestResponse (sync, default), Event (async), or DryRun" + }, + "payload": { + "type": "string", + "description": "JSON payload to pass to the function" + } + }, + "required": [ + "function_name" + ] + } + }, + { + "name": "aws_lambda_list_event_source_mappings", + "description": "List event source mappings for a function", + "inputSchema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Function name or ARN" + } + } + } + }, + { + "name": "aws_lambda_get_function_configuration", + "description": "Get the configuration of a Lambda function", + "inputSchema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Function name or ARN" + } + }, + "required": [ + "function_name" + ] + } + }, + { + "name": "aws_iam_list_users", + "description": "List IAM users. Start here for access audits and finding who has AWS access.", + "inputSchema": { + "type": "object", + "properties": { + "max_items": { + "type": "string", + "description": "Maximum number of users to return" + }, + "path_prefix": { + "type": "string", + "description": "Path prefix for filtering (default /)" + } + } + } + }, + { + "name": "aws_iam_get_user", + "description": "Get details about an IAM user", + "inputSchema": { + "type": "object", + "properties": { + "username": { + "type": "string", + "description": "IAM username (omit for current user)" + } + } + } + }, + { + "name": "aws_iam_list_roles", + "description": "List IAM roles", + "inputSchema": { + "type": "object", + "properties": { + "max_items": { + "type": "string", + "description": "Maximum number of roles to return" + }, + "path_prefix": { + "type": "string", + "description": "Path prefix for filtering (default /)" + } + } + } + }, + { + "name": "aws_iam_get_role", + "description": "Get details about an IAM role", + "inputSchema": { + "type": "object", + "properties": { + "role_name": { + "type": "string", + "description": "IAM role name" + } + }, + "required": [ + "role_name" + ] + } + }, + { + "name": "aws_iam_list_policies", + "description": "List IAM policies", + "inputSchema": { + "type": "object", + "properties": { + "max_items": { + "type": "string", + "description": "Maximum number of policies to return" + }, + "only_attached": { + "type": "string", + "description": "Only show attached policies (true/false)" + }, + "path_prefix": { + "type": "string", + "description": "Path prefix filter" + }, + "scope": { + "type": "string", + "description": "Scope: All, AWS, Local (default All)" + } + } + } + }, + { + "name": "aws_iam_get_policy", + "description": "Get details about an IAM policy", + "inputSchema": { + "type": "object", + "properties": { + "policy_arn": { + "type": "string", + "description": "Policy ARN" + } + }, + "required": [ + "policy_arn" + ] + } + }, + { + "name": "aws_iam_list_groups", + "description": "List IAM groups", + "inputSchema": { + "type": "object", + "properties": { + "max_items": { + "type": "string", + "description": "Maximum number of groups to return" + }, + "path_prefix": { + "type": "string", + "description": "Path prefix for filtering" + } + } + } + }, + { + "name": "aws_iam_list_attached_role_policies", + "description": "List policies attached to an IAM role", + "inputSchema": { + "type": "object", + "properties": { + "path_prefix": { + "type": "string", + "description": "Path prefix filter" + }, + "role_name": { + "type": "string", + "description": "IAM role name" + } + }, + "required": [ + "role_name" + ] + } + }, + { + "name": "aws_iam_list_attached_user_policies", + "description": "List policies attached to an IAM user", + "inputSchema": { + "type": "object", + "properties": { + "path_prefix": { + "type": "string", + "description": "Path prefix filter" + }, + "username": { + "type": "string", + "description": "IAM username" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "aws_iam_list_attached_group_policies", + "description": "List policies attached to an IAM group", + "inputSchema": { + "type": "object", + "properties": { + "group_name": { + "type": "string", + "description": "IAM group name" + }, + "path_prefix": { + "type": "string", + "description": "Path prefix filter" + } + }, + "required": [ + "group_name" + ] + } + }, + { + "name": "aws_cloudwatch_list_metrics", + "description": "List CloudWatch metrics", + "inputSchema": { + "type": "object", + "properties": { + "metric_name": { + "type": "string", + "description": "Metric name filter" + }, + "namespace": { + "type": "string", + "description": "Metric namespace (e.g. AWS/EC2)" + } + } + } + }, + { + "name": "aws_cloudwatch_get_metric_data", + "description": "Get CloudWatch metric data points (time series). Use for monitoring performance over a time range. Use after list_metrics to discover available metrics.", + "inputSchema": { + "type": "object", + "properties": { + "dimensions": { + "type": "string", + "description": "JSON object of dimension key-value pairs" + }, + "end_time": { + "type": "string", + "description": "End time (RFC3339 or relative, default now)" + }, + "metric_name": { + "type": "string", + "description": "Metric name" + }, + "namespace": { + "type": "string", + "description": "Metric namespace" + }, + "period": { + "type": "string", + "description": "Period in seconds (default 300)" + }, + "start_time": { + "type": "string", + "description": "Start time (RFC3339 or relative e.g. -1h)" + }, + "stat": { + "type": "string", + "description": "Statistic: Average, Sum, Minimum, Maximum, SampleCount" + } + }, + "required": [ + "metric_name", + "namespace", + "stat" + ] + } + }, + { + "name": "aws_cloudwatch_describe_alarms", + "description": "List CloudWatch alarms for active alerts, firing monitors, and threshold warnings. Filter by state (ALARM, OK, INSUFFICIENT_DATA).", + "inputSchema": { + "type": "object", + "properties": { + "alarm_names": { + "type": "string", + "description": "Comma-separated alarm names" + }, + "max_records": { + "type": "string", + "description": "Maximum number of alarms to return" + }, + "state_value": { + "type": "string", + "description": "Filter by state: OK, ALARM, INSUFFICIENT_DATA" + } + } + } + }, + { + "name": "aws_cloudwatch_get_metric_statistics", + "description": "Get statistics for a specific CloudWatch metric", + "inputSchema": { + "type": "object", + "properties": { + "dimensions": { + "type": "string", + "description": "JSON object of dimension key-value pairs" + }, + "end_time": { + "type": "string", + "description": "End time (RFC3339)" + }, + "metric_name": { + "type": "string", + "description": "Metric name" + }, + "namespace": { + "type": "string", + "description": "Metric namespace" + }, + "period": { + "type": "string", + "description": "Period in seconds" + }, + "start_time": { + "type": "string", + "description": "Start time (RFC3339 or relative e.g. -1h)" + }, + "statistics": { + "type": "string", + "description": "Comma-separated: Average, Sum, Minimum, Maximum, SampleCount" + } + }, + "required": [ + "metric_name", + "namespace", + "period", + "start_time", + "statistics" + ] + } + }, + { + "name": "aws_ecs_list_clusters", + "description": "List ECS clusters", + "inputSchema": { + "type": "object" + } + }, + { + "name": "aws_ecs_describe_clusters", + "description": "Get details about one or more ECS clusters", + "inputSchema": { + "type": "object", + "properties": { + "clusters": { + "type": "string", + "description": "Comma-separated cluster names or ARNs" + } + }, + "required": [ + "clusters" + ] + } + }, + { + "name": "aws_ecs_list_services", + "description": "List services (deployed containers) in an ECS cluster. Use after list_clusters to find the cluster name.", + "inputSchema": { + "type": "object", + "properties": { + "cluster": { + "type": "string", + "description": "Cluster name or ARN" + } + }, + "required": [ + "cluster" + ] + } + }, + { + "name": "aws_ecs_describe_services", + "description": "Get details about ECS services including deploy status, health, and running/desired count. Use after list_services.", + "inputSchema": { + "type": "object", + "properties": { + "cluster": { + "type": "string", + "description": "Cluster name or ARN" + }, + "services": { + "type": "string", + "description": "Comma-separated service names or ARNs" + } + }, + "required": [ + "cluster", + "services" + ] + } + }, + { + "name": "aws_ecs_list_tasks", + "description": "List tasks in an ECS cluster", + "inputSchema": { + "type": "object", + "properties": { + "cluster": { + "type": "string", + "description": "Cluster name or ARN" + }, + "desired_status": { + "type": "string", + "description": "Filter by status: RUNNING, PENDING, STOPPED" + }, + "service_name": { + "type": "string", + "description": "Filter by service name" + } + } + } + }, + { + "name": "aws_ecs_describe_tasks", + "description": "Get details about one or more ECS tasks", + "inputSchema": { + "type": "object", + "properties": { + "cluster": { + "type": "string", + "description": "Cluster name or ARN" + }, + "tasks": { + "type": "string", + "description": "Comma-separated task IDs or ARNs" + } + }, + "required": [ + "cluster", + "tasks" + ] + } + }, + { + "name": "aws_ecs_list_task_definitions", + "description": "List ECS task definition families or revisions", + "inputSchema": { + "type": "object", + "properties": { + "family_prefix": { + "type": "string", + "description": "Task definition family prefix filter" + }, + "status": { + "type": "string", + "description": "Filter: ACTIVE or INACTIVE" + } + } + } + }, + { + "name": "aws_ecs_describe_task_definition", + "description": "Get details about an ECS task definition", + "inputSchema": { + "type": "object", + "properties": { + "task_definition": { + "type": "string", + "description": "Task definition family:revision or full ARN" + } + }, + "required": [ + "task_definition" + ] + } + }, + { + "name": "aws_sns_list_topics", + "description": "List SNS topics", + "inputSchema": { + "type": "object" + } + }, + { + "name": "aws_sns_get_topic_attributes", + "description": "Get attributes for an SNS topic", + "inputSchema": { + "type": "object", + "properties": { + "topic_arn": { + "type": "string", + "description": "SNS topic ARN" + } + }, + "required": [ + "topic_arn" + ] + } + }, + { + "name": "aws_sns_list_subscriptions", + "description": "List SNS subscriptions", + "inputSchema": { + "type": "object", + "properties": { + "topic_arn": { + "type": "string", + "description": "Filter by topic ARN" + } + } + } + }, + { + "name": "aws_sns_publish", + "description": "Publish (send) a notification message to an SNS topic. Use after list_topics to find the topic ARN.", + "inputSchema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message body" + }, + "subject": { + "type": "string", + "description": "Message subject (for email subscriptions)" + }, + "topic_arn": { + "type": "string", + "description": "SNS topic ARN" + } + }, + "required": [ + "message", + "topic_arn" + ] + } + }, + { + "name": "aws_sqs_list_queues", + "description": "List SQS queues", + "inputSchema": { + "type": "object", + "properties": { + "queue_name_prefix": { + "type": "string", + "description": "Queue name prefix filter" + } + } + } + }, + { + "name": "aws_sqs_get_queue_attributes", + "description": "Get attributes for an SQS queue", + "inputSchema": { + "type": "object", + "properties": { + "queue_url": { + "type": "string", + "description": "SQS queue URL" + } + }, + "required": [ + "queue_url" + ] + } + }, + { + "name": "aws_sqs_send_message", + "description": "Send a message to an SQS queue", + "inputSchema": { + "type": "object", + "properties": { + "delay_seconds": { + "type": "string", + "description": "Delay in seconds (0-900)" + }, + "message_body": { + "type": "string", + "description": "Message content" + }, + "queue_url": { + "type": "string", + "description": "SQS queue URL" + } + }, + "required": [ + "message_body", + "queue_url" + ] + } + }, + { + "name": "aws_sqs_receive_message", + "description": "Receive messages from an SQS queue", + "inputSchema": { + "type": "object", + "properties": { + "max_messages": { + "type": "string", + "description": "Max messages to receive (1-10, default 1)" + }, + "queue_url": { + "type": "string", + "description": "SQS queue URL" + }, + "wait_time_seconds": { + "type": "string", + "description": "Long poll wait time (0-20)" + } + }, + "required": [ + "queue_url" + ] + } + }, + { + "name": "aws_sqs_delete_message", + "description": "Delete a message from an SQS queue", + "inputSchema": { + "type": "object", + "properties": { + "queue_url": { + "type": "string", + "description": "SQS queue URL" + }, + "receipt_handle": { + "type": "string", + "description": "Receipt handle from receive" + } + }, + "required": [ + "queue_url", + "receipt_handle" + ] + } + }, + { + "name": "aws_sqs_purge_queue", + "description": "Purge all messages from an SQS queue", + "inputSchema": { + "type": "object", + "properties": { + "queue_url": { + "type": "string", + "description": "SQS queue URL" + } + }, + "required": [ + "queue_url" + ] + } + }, + { + "name": "aws_dynamodb_list_tables", + "description": "List DynamoDB tables", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Maximum number of tables to return" + } + } + } + }, + { + "name": "aws_dynamodb_describe_table", + "description": "Get details about a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table_name" + ] + } + }, + { + "name": "aws_dynamodb_get_item", + "description": "Get an item from a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "JSON object with key attributes (e.g. {\"id\":{\"S\":\"123\"}})" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "key", + "table_name" + ] + } + }, + { + "name": "aws_dynamodb_put_item", + "description": "Put an item into a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "item": { + "type": "string", + "description": "JSON object with item attributes in DynamoDB format" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "item", + "table_name" + ] + } + }, + { + "name": "aws_dynamodb_query", + "description": "Query a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "expression_attribute_names": { + "type": "string", + "description": "JSON object of expression attribute name placeholders" + }, + "expression_attribute_values": { + "type": "string", + "description": "JSON object of expression attribute values in DynamoDB format" + }, + "index_name": { + "type": "string", + "description": "Secondary index name" + }, + "key_condition_expression": { + "type": "string", + "description": "Key condition expression" + }, + "limit": { + "type": "string", + "description": "Maximum number of items to return" + }, + "scan_index_forward": { + "type": "string", + "description": "Sort ascending (true, default) or descending (false)" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "expression_attribute_values", + "key_condition_expression", + "table_name" + ] + } + }, + { + "name": "aws_dynamodb_scan", + "description": "Scan a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "expression_attribute_names": { + "type": "string", + "description": "JSON object of expression attribute name placeholders" + }, + "expression_attribute_values": { + "type": "string", + "description": "JSON object of expression attribute values" + }, + "filter_expression": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum number of items to return" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table_name" + ] + } + }, + { + "name": "aws_dynamodb_delete_item", + "description": "Delete an item from a DynamoDB table", + "inputSchema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "JSON object with key attributes in DynamoDB format" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "key", + "table_name" + ] + } + }, + { + "name": "aws_cloudformation_list_stacks", + "description": "List CloudFormation stacks", + "inputSchema": { + "type": "object", + "properties": { + "status_filter": { + "type": "string", + "description": "Comma-separated status filter (e.g. CREATE_COMPLETE,UPDATE_COMPLETE)" + } + } + } + }, + { + "name": "aws_cloudformation_describe_stack", + "description": "Get details about a CloudFormation stack", + "inputSchema": { + "type": "object", + "properties": { + "stack_name": { + "type": "string", + "description": "Stack name or ID" + } + }, + "required": [ + "stack_name" + ] + } + }, + { + "name": "aws_cloudformation_list_stack_resources", + "description": "List resources in a CloudFormation stack", + "inputSchema": { + "type": "object", + "properties": { + "stack_name": { + "type": "string", + "description": "Stack name or ID" + } + }, + "required": [ + "stack_name" + ] + } + }, + { + "name": "aws_cloudformation_get_template", + "description": "Get the template for a CloudFormation stack", + "inputSchema": { + "type": "object", + "properties": { + "stack_name": { + "type": "string", + "description": "Stack name or ID" + } + }, + "required": [ + "stack_name" + ] + } + }, + { + "name": "aws_cloudformation_describe_stack_events", + "description": "List events for a CloudFormation stack. Use to debug deploy failures, rollbacks, or track infrastructure change history.", + "inputSchema": { + "type": "object", + "properties": { + "stack_name": { + "type": "string", + "description": "Stack name or ID" + } + }, + "required": [ + "stack_name" + ] + } + }, + { + "name": "posthog_list_projects", + "description": "List all projects in the PostHog organization. Start here to discover projects.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + } + } + } + }, + { + "name": "posthog_get_project", + "description": "Get details of a specific project", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_create_project", + "description": "Create a new project", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Project name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "posthog_update_project", + "description": "Update a project's settings", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "New name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_delete_project", + "description": "Delete a project", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Project ID" + } + }, + "required": [ + "project_id" + ] + } + }, + { + "name": "posthog_list_feature_flags", + "description": "List feature flags for rollout targeting and release management. Filter by active state, type, or experiment.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Filter by active state (true/false)" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "search": { + "type": "string", + "description": "Search by key or name" + }, + "type": { + "type": "string", + "description": "Filter by type: boolean, multivariant, experiment" + } + } + } + }, + { + "name": "posthog_get_feature_flag", + "description": "Get details of a specific feature flag", + "inputSchema": { + "type": "object", + "properties": { + "flag_id": { + "type": "string", + "description": "Feature flag ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "flag_id" + ] + } + }, + { + "name": "posthog_create_feature_flag", + "description": "Create a new feature flag", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Whether flag is active (true/false)" + }, + "ensure_experience_continuity": { + "type": "string", + "description": "Persist flag value for users across sessions (true/false)" + }, + "filters": { + "type": "string", + "description": "JSON string of filter/rollout config" + }, + "key": { + "type": "string", + "description": "Feature flag key (unique identifier)" + }, + "name": { + "type": "string", + "description": "Human-readable name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "key" + ] + } + }, + { + "name": "posthog_update_feature_flag", + "description": "Update a feature flag", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Whether flag is active (true/false)" + }, + "filters": { + "type": "string", + "description": "JSON string of filter/rollout config" + }, + "flag_id": { + "type": "string", + "description": "Feature flag ID" + }, + "key": { + "type": "string", + "description": "Feature flag key" + }, + "name": { + "type": "string", + "description": "Human-readable name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "flag_id" + ] + } + }, + { + "name": "posthog_delete_feature_flag", + "description": "Delete a feature flag (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "flag_id": { + "type": "string", + "description": "Feature flag ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "flag_id" + ] + } + }, + { + "name": "posthog_feature_flag_activity", + "description": "Get activity log for a feature flag", + "inputSchema": { + "type": "object", + "properties": { + "flag_id": { + "type": "string", + "description": "Feature flag ID" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "flag_id" + ] + } + }, + { + "name": "posthog_list_cohorts", + "description": "List all user cohorts for audience segmentation and analytics targeting", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_get_cohort", + "description": "Get details of a specific cohort", + "inputSchema": { + "type": "object", + "properties": { + "cohort_id": { + "type": "string", + "description": "Cohort ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "cohort_id" + ] + } + }, + { + "name": "posthog_create_cohort", + "description": "Create a new cohort", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Cohort description" + }, + "filters": { + "type": "string", + "description": "JSON string of cohort filters" + }, + "is_static": { + "type": "string", + "description": "Whether cohort is static (true/false)" + }, + "name": { + "type": "string", + "description": "Cohort name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "posthog_update_cohort", + "description": "Update a cohort", + "inputSchema": { + "type": "object", + "properties": { + "cohort_id": { + "type": "string", + "description": "Cohort ID" + }, + "description": { + "type": "string", + "description": "Cohort description" + }, + "filters": { + "type": "string", + "description": "JSON string of cohort filters" + }, + "name": { + "type": "string", + "description": "Cohort name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "cohort_id" + ] + } + }, + { + "name": "posthog_delete_cohort", + "description": "Delete a cohort (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "cohort_id": { + "type": "string", + "description": "Cohort ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "cohort_id" + ] + } + }, + { + "name": "posthog_list_cohort_persons", + "description": "List persons in a cohort", + "inputSchema": { + "type": "object", + "properties": { + "cohort_id": { + "type": "string", + "description": "Cohort ID" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "cohort_id" + ] + } + }, + { + "name": "posthog_list_insights", + "description": "List saved product analytics insights (trends, funnels, retention, etc.). View reports, charts, and metrics.", + "inputSchema": { + "type": "object", + "properties": { + "created_by": { + "type": "string", + "description": "Filter by creator user ID" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "search": { + "type": "string", + "description": "Search by name" + } + } + } + }, + { + "name": "posthog_get_insight", + "description": "Get details of a specific product analytics insight, including chart data for trends, funnels, and retention", + "inputSchema": { + "type": "object", + "properties": { + "insight_id": { + "type": "string", + "description": "Insight ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "insight_id" + ] + } + }, + { + "name": "posthog_create_insight", + "description": "Create a new insight", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Insight description" + }, + "filters": { + "type": "string", + "description": "JSON string of insight filters (events, actions, properties, date ranges)" + }, + "name": { + "type": "string", + "description": "Insight name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "query": { + "type": "string", + "description": "JSON string of HogQL query definition" + } + } + } + }, + { + "name": "posthog_update_insight", + "description": "Update an insight", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Insight description" + }, + "filters": { + "type": "string", + "description": "JSON string of insight filters" + }, + "insight_id": { + "type": "string", + "description": "Insight ID" + }, + "name": { + "type": "string", + "description": "Insight name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "query": { + "type": "string", + "description": "JSON string of HogQL query definition" + } + }, + "required": [ + "insight_id" + ] + } + }, + { + "name": "posthog_delete_insight", + "description": "Delete an insight (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "insight_id": { + "type": "string", + "description": "Insight ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "insight_id" + ] + } + }, + { + "name": "posthog_query", + "description": "Run a HogQL (SQL-like) query synchronously and return inline results. Use this for ad-hoc analytics without persisting an insight. Returns columns, results rows, and execution metadata.", + "inputSchema": { + "type": "object", + "properties": { + "client_query_id": { + "type": "string", + "description": "Optional client-supplied identifier to correlate the request in PostHog logs" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "query": { + "type": "string", + "description": "HogQL query text, e.g. SELECT count() FROM events WHERE timestamp \u003e now() - INTERVAL 7 DAY" + }, + "refresh": { + "type": "string", + "description": "Optional cache mode: 'blocking' (default-ish, wait for fresh result), 'force_blocking', 'lazy_async', 'force_cache'" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "posthog_list_persons", + "description": "List persons (users and customers) tracked by PostHog analytics. Search by email, distinct ID, or visitor profile.", + "inputSchema": { + "type": "object", + "properties": { + "distinct_id": { + "type": "string", + "description": "Filter by exact distinct ID" + }, + "email": { + "type": "string", + "description": "Filter by email" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "search": { + "type": "string", + "description": "Search by email or distinct ID" + } + } + } + }, + { + "name": "posthog_get_person", + "description": "Get details of a specific person", + "inputSchema": { + "type": "object", + "properties": { + "person_id": { + "type": "string", + "description": "Person UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "person_id" + ] + } + }, + { + "name": "posthog_delete_person", + "description": "Delete a person and their data", + "inputSchema": { + "type": "object", + "properties": { + "person_id": { + "type": "string", + "description": "Person UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "person_id" + ] + } + }, + { + "name": "posthog_update_person_property", + "description": "Update a property on a person", + "inputSchema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Property key" + }, + "person_id": { + "type": "string", + "description": "Person UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "value": { + "type": "string", + "description": "Property value" + } + }, + "required": [ + "key", + "person_id", + "value" + ] + } + }, + { + "name": "posthog_delete_person_property", + "description": "Delete a property from a person", + "inputSchema": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Property key to remove" + }, + "person_id": { + "type": "string", + "description": "Person UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "key", + "person_id" + ] + } + }, + { + "name": "posthog_list_groups", + "description": "List groups", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor" + }, + "group_type_index": { + "type": "string", + "description": "Group type index (0-based)" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "search": { + "type": "string", + "description": "Search by group key or properties" + } + } + } + }, + { + "name": "posthog_find_group", + "description": "Find a specific group by key", + "inputSchema": { + "type": "object", + "properties": { + "group_key": { + "type": "string", + "description": "Group key to find" + }, + "group_type_index": { + "type": "string", + "description": "Group type index (0-based)" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "group_key", + "group_type_index" + ] + } + }, + { + "name": "posthog_list_annotations", + "description": "List annotations (markers on charts)", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "search": { + "type": "string", + "description": "Search by content" + } + } + } + }, + { + "name": "posthog_get_annotation", + "description": "Get details of a specific annotation", + "inputSchema": { + "type": "object", + "properties": { + "annotation_id": { + "type": "string", + "description": "Annotation ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "annotation_id" + ] + } + }, + { + "name": "posthog_create_annotation", + "description": "Create a new annotation", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Annotation text" + }, + "date_marker": { + "type": "string", + "description": "ISO 8601 date for the annotation marker" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "scope": { + "type": "string", + "description": "Scope: dashboard_item, project, organization" + } + }, + "required": [ + "content", + "date_marker" + ] + } + }, + { + "name": "posthog_update_annotation", + "description": "Update an annotation", + "inputSchema": { + "type": "object", + "properties": { + "annotation_id": { + "type": "string", + "description": "Annotation ID" + }, + "content": { + "type": "string", + "description": "Annotation text" + }, + "date_marker": { + "type": "string", + "description": "ISO 8601 date" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "scope": { + "type": "string", + "description": "Scope: dashboard_item, project, organization" + } + }, + "required": [ + "annotation_id" + ] + } + }, + { + "name": "posthog_delete_annotation", + "description": "Delete an annotation", + "inputSchema": { + "type": "object", + "properties": { + "annotation_id": { + "type": "string", + "description": "Annotation ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "annotation_id" + ] + } + }, + { + "name": "posthog_list_dashboards", + "description": "List PostHog product analytics dashboards for metrics overview and monitoring", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_get_dashboard", + "description": "Get details of a specific dashboard", + "inputSchema": { + "type": "object", + "properties": { + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "posthog_create_dashboard", + "description": "Create a new dashboard", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Dashboard description" + }, + "name": { + "type": "string", + "description": "Dashboard name" + }, + "pinned": { + "type": "string", + "description": "Pin dashboard (true/false)" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "posthog_update_dashboard", + "description": "Update a dashboard", + "inputSchema": { + "type": "object", + "properties": { + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + }, + "description": { + "type": "string", + "description": "Dashboard description" + }, + "name": { + "type": "string", + "description": "Dashboard name" + }, + "pinned": { + "type": "string", + "description": "Pin dashboard (true/false)" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "posthog_delete_dashboard", + "description": "Delete a dashboard (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "dashboard_id": { + "type": "string", + "description": "Dashboard ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "dashboard_id" + ] + } + }, + { + "name": "posthog_list_actions", + "description": "List actions (custom event groupings) for analytics tracking and conversion metrics", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_get_action", + "description": "Get details of a specific action", + "inputSchema": { + "type": "object", + "properties": { + "action_id": { + "type": "string", + "description": "Action ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "action_id" + ] + } + }, + { + "name": "posthog_create_action", + "description": "Create a new action", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Action description" + }, + "name": { + "type": "string", + "description": "Action name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "steps": { + "type": "string", + "description": "JSON array of action step definitions" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "posthog_update_action", + "description": "Update an action", + "inputSchema": { + "type": "object", + "properties": { + "action_id": { + "type": "string", + "description": "Action ID" + }, + "description": { + "type": "string", + "description": "Action description" + }, + "name": { + "type": "string", + "description": "Action name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "steps": { + "type": "string", + "description": "JSON array of action step definitions" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + } + }, + "required": [ + "action_id" + ] + } + }, + { + "name": "posthog_delete_action", + "description": "Delete an action (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "action_id": { + "type": "string", + "description": "Action ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "action_id" + ] + } + }, + { + "name": "posthog_list_events", + "description": "List captured product analytics events. View user behavior tracking and activity data.", + "inputSchema": { + "type": "object", + "properties": { + "after": { + "type": "string", + "description": "ISO 8601 timestamp lower bound" + }, + "before": { + "type": "string", + "description": "ISO 8601 timestamp upper bound" + }, + "distinct_id": { + "type": "string", + "description": "Filter by distinct ID" + }, + "event": { + "type": "string", + "description": "Filter by event name" + }, + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "person_id": { + "type": "string", + "description": "Filter by person UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "properties": { + "type": "string", + "description": "JSON string of property filters" + } + } + } + }, + { + "name": "posthog_get_event", + "description": "Get details of a specific event", + "inputSchema": { + "type": "object", + "properties": { + "event_id": { + "type": "string", + "description": "Event UUID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "event_id" + ] + } + }, + { + "name": "posthog_list_experiments", + "description": "List A/B test experiments for conversion optimization. View variant results and analytics.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_get_experiment", + "description": "Get details of a specific experiment", + "inputSchema": { + "type": "object", + "properties": { + "experiment_id": { + "type": "string", + "description": "Experiment ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "experiment_id" + ] + } + }, + { + "name": "posthog_create_experiment", + "description": "Create a new experiment", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Experiment description" + }, + "end_date": { + "type": "string", + "description": "ISO 8601 end date" + }, + "feature_flag_key": { + "type": "string", + "description": "Feature flag key to use for experiment" + }, + "filters": { + "type": "string", + "description": "JSON string of experiment filters" + }, + "name": { + "type": "string", + "description": "Experiment name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "start_date": { + "type": "string", + "description": "ISO 8601 start date" + } + }, + "required": [ + "feature_flag_key", + "name" + ] + } + }, + { + "name": "posthog_update_experiment", + "description": "Update an experiment", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Experiment description" + }, + "end_date": { + "type": "string", + "description": "ISO 8601 end date" + }, + "experiment_id": { + "type": "string", + "description": "Experiment ID" + }, + "name": { + "type": "string", + "description": "Experiment name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "start_date": { + "type": "string", + "description": "ISO 8601 start date" + } + }, + "required": [ + "experiment_id" + ] + } + }, + { + "name": "posthog_delete_experiment", + "description": "Delete an experiment (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "experiment_id": { + "type": "string", + "description": "Experiment ID" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + }, + "required": [ + "experiment_id" + ] + } + }, + { + "name": "posthog_list_surveys", + "description": "List surveys", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max results" + }, + "offset": { + "type": "string", + "description": "Pagination offset" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + } + } + } + }, + { + "name": "posthog_get_survey", + "description": "Get details of a specific survey", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "survey_id": { + "type": "string", + "description": "Survey ID" + } + }, + "required": [ + "survey_id" + ] + } + }, + { + "name": "posthog_create_survey", + "description": "Create a new survey", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Survey description" + }, + "name": { + "type": "string", + "description": "Survey name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "questions": { + "type": "string", + "description": "JSON array of question definitions" + }, + "targeting_flag_filters": { + "type": "string", + "description": "JSON string of targeting filters" + }, + "type": { + "type": "string", + "description": "Survey type" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "posthog_update_survey", + "description": "Update a survey", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Survey description" + }, + "name": { + "type": "string", + "description": "Survey name" + }, + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "questions": { + "type": "string", + "description": "JSON array of question definitions" + }, + "survey_id": { + "type": "string", + "description": "Survey ID" + } + }, + "required": [ + "survey_id" + ] + } + }, + { + "name": "posthog_delete_survey", + "description": "Delete a survey (soft delete)", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Project ID (uses default if configured, otherwise required)" + }, + "survey_id": { + "type": "string", + "description": "Survey ID" + } + }, + "required": [ + "survey_id" + ] + } + }, + { + "name": "postgres_list_databases", + "description": "List all configured database connections with their alias, host, database name, and read-only status", + "inputSchema": { + "type": "object" + } + }, + { + "name": "postgres_list_schemas", + "description": "List all schemas in the database. Start here for schema discovery.", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + } + } + } + }, + { + "name": "postgres_list_tables", + "description": "List all tables in a schema with row counts and size estimates", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + } + } + } + }, + { + "name": "postgres_describe_table", + "description": "Get detailed column info for a table including types, nullability, defaults, and constraints", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_columns", + "description": "List all columns for a table with data types and ordinal positions", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_indexes", + "description": "List all indexes on a table with definitions and size", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_constraints", + "description": "List all constraints (primary key, foreign key, unique, check) on a table", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_foreign_keys", + "description": "List all foreign key relationships for a table (both referencing and referenced)", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_views", + "description": "List all views in a schema with their definitions", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + } + } + } + }, + { + "name": "postgres_list_functions", + "description": "List user-defined functions in a schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + } + } + } + }, + { + "name": "postgres_list_triggers", + "description": "List all triggers on a table or in a schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name (optional, lists all triggers in schema if omitted)" + } + } + } + }, + { + "name": "postgres_list_enums", + "description": "List all enum types in the database with their values", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + } + } + } + }, + { + "name": "postgres_query", + "description": "Execute a read-only SQL query and return results as JSON. Use for database exploration and performance investigation. Automatically wrapped in a read-only transaction.", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "limit": { + "type": "string", + "description": "Max rows to return (default: 100, max: 1000)" + }, + "sql": { + "type": "string", + "description": "SQL query to execute (SELECT, SHOW, EXPLAIN, etc.)" + } + }, + "required": [ + "sql" + ] + } + }, + { + "name": "postgres_execute", + "description": "Execute a data-modifying SQL statement (INSERT, UPDATE, DELETE, CREATE, ALTER, DROP). Returns rows affected. **CAUTION: executes arbitrary SQL including DDL/DML. Disabled by default -- set read_only=false in credentials to enable.** DROP DATABASE and TRUNCATE are always denied.", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "sql": { + "type": "string", + "description": "SQL statement to execute" + } + }, + "required": [ + "sql" + ] + } + }, + { + "name": "postgres_explain", + "description": "Run EXPLAIN ANALYZE on a SQL query to show the execution plan with actual timing. Use to diagnose slow queries and optimize database performance.", + "inputSchema": { + "type": "object", + "properties": { + "analyze": { + "type": "string", + "description": "Run EXPLAIN ANALYZE with actual execution (default: false)" + }, + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "format": { + "type": "string", + "description": "Output format: text, json, yaml, xml (default: text)" + }, + "sql": { + "type": "string", + "description": "SQL query to explain" + } + }, + "required": [ + "sql" + ] + } + }, + { + "name": "postgres_select", + "description": "Select rows from a table with optional filtering, ordering, and pagination. The columns, where, and order_by parameters accept SQL expressions (semicolons and comments are rejected).", + "inputSchema": { + "type": "object", + "properties": { + "columns": { + "type": "string", + "description": "Comma-separated column names or SQL expressions (default: *)" + }, + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "limit": { + "type": "string", + "description": "Max rows to return (default: 100)" + }, + "offset": { + "type": "string", + "description": "Number of rows to skip" + }, + "order_by": { + "type": "string", + "description": "ORDER BY clause without the ORDER BY keyword (SQL expression)" + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + }, + "where": { + "type": "string", + "description": "WHERE clause without the WHERE keyword (SQL expression)" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_database_info", + "description": "Get database-level info: version, current database, current user, server settings", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + } + } + } + }, + { + "name": "postgres_database_size", + "description": "Get the size of the current database and its largest tables", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "limit": { + "type": "string", + "description": "Number of largest tables to return (default: 20)" + } + } + } + }, + { + "name": "postgres_table_stats", + "description": "Get detailed statistics for a table including row count, dead tuples, last vacuum/analyze times", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "postgres_list_roles", + "description": "List all database roles with their attributes (superuser, createdb, login, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + } + } + } + }, + { + "name": "postgres_list_grants", + "description": "List privileges granted on a table or schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "schema": { + "type": "string", + "description": "Schema name (default: public)" + }, + "table": { + "type": "string", + "description": "Table name (optional, shows schema-level grants if omitted)" + } + } + } + }, + { + "name": "postgres_list_extensions", + "description": "List all installed extensions with versions", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + } + } + } + }, + { + "name": "postgres_list_active_connections", + "description": "List active database connections with query state, duration, and client info", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "state": { + "type": "string", + "description": "Filter by state: active, idle, idle in transaction (optional)" + } + } + } + }, + { + "name": "postgres_list_locks", + "description": "List current lock activity showing blocked and blocking queries", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + } + } + } + }, + { + "name": "postgres_running_queries", + "description": "List currently running queries with duration and state. Use to find slow or long-running queries that may be blocking database operations.", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Connection alias (omit to use default). Use postgres_list_databases to see available connections." + }, + "min_duration": { + "type": "string", + "description": "Minimum duration in seconds to filter by (optional)" + } + } + } + }, + { + "name": "clickhouse_list_connections", + "description": "List all configured ClickHouse cluster connections with their alias, host, database, TLS, and default status. Start here when choosing which cluster to query.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_execute_query", + "description": "Execute a SQL query (SELECT, SHOW, DESCRIBE, or DDL) against a ClickHouse analytics database and return results as JSON rows", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Optional database context to run the query in (overrides configured default)" + }, + "query": { + "type": "string", + "description": "SQL query string to execute" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "clickhouse_explain_query", + "description": "Run EXPLAIN on a query to show the execution plan", + "inputSchema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "SQL query to explain" + }, + "type": { + "type": "string", + "description": "EXPLAIN type: PLAN (default), PIPELINE, SYNTAX, AST, ESTIMATE" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "clickhouse_list_databases", + "description": "List all databases in the ClickHouse server. Start here for schema discovery.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_tables", + "description": "List all tables in a database with engine, row count, and size", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to current database)" + } + } + } + }, + { + "name": "clickhouse_describe_table", + "description": "Describe a table's columns with names, types, default expressions, and comments", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to current database)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "clickhouse_list_columns", + "description": "List detailed column metadata for a table from system.columns", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to current database)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "clickhouse_show_create_table", + "description": "Show the CREATE TABLE statement for a table", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to current database)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "clickhouse_server_info", + "description": "Get ClickHouse server version, uptime, and OS info", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_processes", + "description": "List currently running queries/processes", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_kill_query", + "description": "Kill a running query by its query ID", + "inputSchema": { + "type": "object", + "properties": { + "query_id": { + "type": "string", + "description": "The query_id of the query to kill" + } + }, + "required": [ + "query_id" + ] + } + }, + { + "name": "clickhouse_list_settings", + "description": "List ClickHouse server settings. Optionally filter by name pattern", + "inputSchema": { + "type": "object", + "properties": { + "pattern": { + "type": "string", + "description": "Optional LIKE pattern to filter setting names (e.g. '%memory%')" + } + } + } + }, + { + "name": "clickhouse_list_merges", + "description": "List currently running background merges for MergeTree tables", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_replicas", + "description": "List replica status for replicated tables", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_disk_usage", + "description": "Show disk usage per database (total bytes, row counts, part counts)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_parts", + "description": "List table parts with sizes for a given table (useful for debugging MergeTree)", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Only show active parts (default: true)" + }, + "database": { + "type": "string", + "description": "Database name (defaults to current database)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "clickhouse_list_dictionaries", + "description": "List external dictionaries loaded in the server", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_users", + "description": "List all users configured in ClickHouse", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_list_roles", + "description": "List all roles configured in ClickHouse", + "inputSchema": { + "type": "object" + } + }, + { + "name": "clickhouse_query_log", + "description": "Search recent entries from system.query_log", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max rows to return (default: 50)" + }, + "query_pattern": { + "type": "string", + "description": "Optional LIKE pattern to filter by query text" + }, + "query_type": { + "type": "string", + "description": "Filter by type: QueryStart, QueryFinish, ExceptionBeforeStart, ExceptionWhileProcessing" + } + } + } + }, + { + "name": "elasticsearch_cluster_health", + "description": "Get cluster health status (green/yellow/red), node count, shard counts, and pending tasks", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_cluster_stats", + "description": "Get cluster-wide statistics including indices count, document count, store size, and node info", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_node_stats", + "description": "Get statistics for all nodes including JVM heap, CPU, disk, and indexing metrics", + "inputSchema": { + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Optional node ID or name to filter (omit for all nodes)" + } + } + } + }, + { + "name": "elasticsearch_cat_nodes", + "description": "Get a compact summary of each node: name, IP, heap, RAM, CPU, load, role, and version", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_pending_tasks", + "description": "List pending cluster-level tasks (e.g. shard allocation, mapping updates)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_list_indices", + "description": "List all indices with health, status, document count, and store size. Start here for index discovery", + "inputSchema": { + "type": "object", + "properties": { + "pattern": { + "type": "string", + "description": "Optional index name pattern with wildcards (e.g. 'logs-*'). Omit for all indices" + } + } + } + }, + { + "name": "elasticsearch_get_index", + "description": "Get full index configuration including settings, mappings, and aliases", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_create_index", + "description": "Create a new index with optional settings (shards, replicas) and field mappings", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name to create" + }, + "mappings": { + "type": "string", + "description": "Optional JSON object for field mappings (e.g. {\"properties\": {\"title\": {\"type\": \"text\"}}})" + }, + "settings": { + "type": "string", + "description": "Optional JSON object for index settings (e.g. {\"number_of_shards\": 1})" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_delete_index", + "description": "Delete an index and all its data. This action is irreversible", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name to delete" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_get_mapping", + "description": "Get field mappings for an index — shows field names, types, and analyzers", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_put_mapping", + "description": "Add new fields to an existing index mapping. Cannot change existing field types", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + }, + "properties": { + "type": "string", + "description": "JSON object of field definitions (e.g. {\"new_field\": {\"type\": \"keyword\"}})" + } + }, + "required": [ + "index", + "properties" + ] + } + }, + { + "name": "elasticsearch_get_settings", + "description": "Get index settings (shards, replicas, refresh interval, analysis config)", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_put_settings", + "description": "Update dynamic index settings (e.g. number_of_replicas, refresh_interval)", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + }, + "settings": { + "type": "string", + "description": "JSON object of settings to update (e.g. {\"index\": {\"number_of_replicas\": 2}})" + } + }, + "required": [ + "index", + "settings" + ] + } + }, + { + "name": "elasticsearch_index_stats", + "description": "Get indexing, search, merge, and segment statistics for an index", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_open_index", + "description": "Open a previously closed index to make it available for search and indexing", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_close_index", + "description": "Close an index to reduce cluster overhead. Closed indices cannot be searched", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_refresh_index", + "description": "Refresh an index to make recent changes visible to search immediately", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_forcemerge_index", + "description": "Force merge an index to reduce segment count. Useful for read-only indices", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name" + }, + "max_segments": { + "type": "string", + "description": "Optional max number of segments to merge down to (default: 1)" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_get_document", + "description": "Get a single document by its ID from an index", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Document ID" + }, + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "id", + "index" + ] + } + }, + { + "name": "elasticsearch_index_document", + "description": "Index (create or replace) a document. If no ID is provided, one is auto-generated", + "inputSchema": { + "type": "object", + "properties": { + "document": { + "type": "string", + "description": "JSON object — the document body to index" + }, + "id": { + "type": "string", + "description": "Optional document ID (auto-generated if omitted)" + }, + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "document", + "index" + ] + } + }, + { + "name": "elasticsearch_update_document", + "description": "Partially update a document by merging fields. Use 'doc' for partial updates or 'script' for scripted updates", + "inputSchema": { + "type": "object", + "properties": { + "doc": { + "type": "string", + "description": "JSON object of fields to merge into the existing document" + }, + "id": { + "type": "string", + "description": "Document ID" + }, + "index": { + "type": "string", + "description": "Index name" + }, + "script": { + "type": "string", + "description": "Optional Painless script for scripted updates (e.g. {\"source\": \"ctx._source.count++\"})" + } + }, + "required": [ + "id", + "index" + ] + } + }, + { + "name": "elasticsearch_delete_document", + "description": "Delete a single document by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Document ID" + }, + "index": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "id", + "index" + ] + } + }, + { + "name": "elasticsearch_bulk", + "description": "Perform multiple index/create/update/delete operations in a single request. Each action is a JSON object with the operation type as key", + "inputSchema": { + "type": "object", + "properties": { + "operations": { + "type": "string", + "description": "JSON array of bulk operations. Each element is {\"action\": \"index|create|update|delete\", \"index\": \"...\", \"id\": \"...\", \"doc\": {...}}" + } + }, + "required": [ + "operations" + ] + } + }, + { + "name": "elasticsearch_mget", + "description": "Get multiple documents by ID in a single request", + "inputSchema": { + "type": "object", + "properties": { + "docs": { + "type": "string", + "description": "JSON array of {\"_index\": \"...\", \"_id\": \"...\"} objects. _index can be omitted if index param is set" + }, + "index": { + "type": "string", + "description": "Optional default index name" + } + }, + "required": [ + "docs" + ] + } + }, + { + "name": "elasticsearch_count", + "description": "Count documents matching a query. Returns total count without fetching documents", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name or pattern (e.g. 'logs-*')" + }, + "query": { + "type": "string", + "description": "Optional Elasticsearch query DSL as JSON (omit to count all documents)" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_delete_by_query", + "description": "Delete all documents matching a query. Use with caution — this is irreversible", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name or pattern" + }, + "query": { + "type": "string", + "description": "Elasticsearch query DSL as JSON to select documents for deletion" + } + }, + "required": [ + "index", + "query" + ] + } + }, + { + "name": "elasticsearch_update_by_query", + "description": "Update all documents matching a query using a script", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Index name or pattern" + }, + "query": { + "type": "string", + "description": "Elasticsearch query DSL as JSON to select documents" + }, + "script": { + "type": "string", + "description": "Painless script for the update (e.g. {\"source\": \"ctx._source.status = 'archived'\"})" + } + }, + "required": [ + "index", + "query", + "script" + ] + } + }, + { + "name": "elasticsearch_reindex", + "description": "Copy documents from one index to another, optionally transforming with a script or filtering with a query. Runs asynchronously — returns a task ID that can be monitored with elasticsearch_list_tasks", + "inputSchema": { + "type": "object", + "properties": { + "dest_index": { + "type": "string", + "description": "Destination index name" + }, + "query": { + "type": "string", + "description": "Optional query DSL to filter source documents" + }, + "script": { + "type": "string", + "description": "Optional Painless script to transform documents during reindex" + }, + "source_index": { + "type": "string", + "description": "Source index name" + } + }, + "required": [ + "dest_index", + "source_index" + ] + } + }, + { + "name": "elasticsearch_search", + "description": "Search documents using Elasticsearch Query DSL. Supports full-text search, filters, aggregations, sorting, and highlighting. Start here for querying data", + "inputSchema": { + "type": "object", + "properties": { + "_source": { + "type": "string", + "description": "Optional source filtering — JSON array of field names to include, or false to exclude _source" + }, + "aggs": { + "type": "string", + "description": "Optional aggregations as JSON object" + }, + "from": { + "type": "string", + "description": "Offset for pagination (default: 0)" + }, + "highlight": { + "type": "string", + "description": "Optional highlight configuration as JSON object" + }, + "index": { + "type": "string", + "description": "Index name or pattern (e.g. 'logs-*')" + }, + "query": { + "type": "string", + "description": "Elasticsearch query DSL as JSON (e.g. {\"match\": {\"title\": \"search term\"}})" + }, + "size": { + "type": "string", + "description": "Max results to return (default: 10, max: 10000)" + }, + "sort": { + "type": "string", + "description": "Optional sort as JSON array (e.g. [{\"timestamp\": \"desc\"}])" + } + }, + "required": [ + "index" + ] + } + }, + { + "name": "elasticsearch_msearch", + "description": "Execute multiple searches in a single request. More efficient than individual search calls", + "inputSchema": { + "type": "object", + "properties": { + "searches": { + "type": "string", + "description": "JSON array of search objects, each with optional 'index' and required 'body' (query DSL)" + } + }, + "required": [ + "searches" + ] + } + }, + { + "name": "elasticsearch_sql_query", + "description": "Execute a SQL query against Elasticsearch using the SQL plugin. Useful for analysts familiar with SQL", + "inputSchema": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "Response format: json (default), csv, tsv, txt, yaml" + }, + "query": { + "type": "string", + "description": "SQL query string (e.g. \"SELECT * FROM my_index WHERE status = 'active' LIMIT 10\")" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "elasticsearch_list_aliases", + "description": "List all index aliases or aliases for a specific index", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Optional index name to filter aliases" + } + } + } + }, + { + "name": "elasticsearch_update_aliases", + "description": "Atomically add or remove index aliases. Useful for zero-downtime reindexing", + "inputSchema": { + "type": "object", + "properties": { + "actions": { + "type": "string", + "description": "JSON array of alias actions (e.g. [{\"add\": {\"index\": \"idx-v2\", \"alias\": \"idx\"}}, {\"remove\": {\"index\": \"idx-v1\", \"alias\": \"idx\"}}])" + } + }, + "required": [ + "actions" + ] + } + }, + { + "name": "elasticsearch_list_index_templates", + "description": "List all index templates with their patterns, priority, and settings", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_get_index_template", + "description": "Get a specific index template definition including patterns, settings, mappings, and aliases", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Template name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "elasticsearch_list_snapshot_repos", + "description": "List registered snapshot repositories and their types", + "inputSchema": { + "type": "object" + } + }, + { + "name": "elasticsearch_list_snapshots", + "description": "List snapshots in a repository with status, indices, and timing", + "inputSchema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Snapshot repository name" + } + }, + "required": [ + "repository" + ] + } + }, + { + "name": "elasticsearch_list_tasks", + "description": "List currently running tasks (reindex, update-by-query, etc.) with progress info", + "inputSchema": { + "type": "object", + "properties": { + "actions": { + "type": "string", + "description": "Optional comma-separated action patterns to filter (e.g. '*reindex*')" + } + } + } + }, + { + "name": "elasticsearch_cancel_task", + "description": "Cancel a running task by its task ID", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID (e.g. 'node_id:task_number')" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "elasticsearch_cat_shards", + "description": "Get shard allocation details: index, shard number, primary/replica, state, size, node", + "inputSchema": { + "type": "object", + "properties": { + "index": { + "type": "string", + "description": "Optional index name to filter" + } + } + } + }, + { + "name": "elasticsearch_cat_allocation", + "description": "Get disk allocation per node: shards, disk used/available, host, IP", + "inputSchema": { + "type": "object" + } + }, + { + "name": "pganalyze_list_servers", + "description": "List monitored PostgreSQL servers in pganalyze. Start here to discover server IDs and database IDs needed by other tools.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "pganalyze_get_server_details", + "description": "Get details for a specific monitored PostgreSQL server including configuration and snapshot info.", + "inputSchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "description": "The server ID (from pganalyze_list_servers)" + } + }, + "required": [ + "server_id" + ] + } + }, + { + "name": "pganalyze_get_postgres_settings", + "description": "Get PostgreSQL configuration settings (e.g. shared_buffers, work_mem) for a server.", + "inputSchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "description": "The server ID (from pganalyze_list_servers)" + } + }, + "required": [ + "server_id" + ] + } + }, + { + "name": "pganalyze_get_databases", + "description": "List databases with size stats and issue counts for a server.", + "inputSchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "description": "The server ID (from pganalyze_list_servers)" + } + }, + "required": [ + "server_id" + ] + } + }, + { + "name": "pganalyze_get_query_stats", + "description": "Get top queries by runtime percentage. Shows expensive and slow query bottlenecks sorted by impact.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID (from pganalyze_list_servers or pganalyze_get_databases)" + }, + "limit": { + "type": "string", + "description": "Number of queries to return (default: 10)" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "pganalyze_get_query_details", + "description": "Get the full normalized query text for a specific query.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "query_id": { + "type": "string", + "description": "Query ID (from pganalyze_get_query_stats)" + } + }, + "required": [ + "database_id", + "query_id" + ] + } + }, + { + "name": "pganalyze_get_query_samples", + "description": "Get sample executions for a query with runtime and parameters.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "query_id": { + "type": "string", + "description": "Query ID (from pganalyze_get_query_stats)" + } + }, + "required": [ + "database_id", + "query_id" + ] + } + }, + { + "name": "pganalyze_get_tables", + "description": "List tables with filtering and pagination for a database.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "limit": { + "type": "string", + "description": "Number of tables to return (optional)" + }, + "schema_name": { + "type": "string", + "description": "Filter by schema name (optional)" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "pganalyze_get_table", + "description": "Get detailed information about a single table: schema details, columns with per-column stats, indexes, and constraints.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "schema_name": { + "type": "string", + "description": "Schema name" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "database_id", + "schema_name", + "table_name" + ] + } + }, + { + "name": "pganalyze_get_table_stats", + "description": "Get time-series table statistics (row counts, dead tuples, sequential scans, etc.).", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "schema_name": { + "type": "string", + "description": "Schema name" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "database_id", + "schema_name", + "table_name" + ] + } + }, + { + "name": "pganalyze_get_index_selection", + "description": "Get Index Advisor results for an existing run. Shows recommended indexes.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "pganalyze_run_index_selection", + "description": "Run the Index Advisor for a table to get index recommendations.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "schema_name": { + "type": "string", + "description": "Schema name" + }, + "table_name": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "database_id", + "schema_name", + "table_name" + ] + } + }, + { + "name": "pganalyze_get_query_explains", + "description": "List EXPLAIN plans for a query (last 7 days). Use to find query plan changes and regressions.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "query_id": { + "type": "string", + "description": "Query ID (from pganalyze_get_query_stats)" + } + }, + "required": [ + "database_id", + "query_id" + ] + } + }, + { + "name": "pganalyze_get_query_explain", + "description": "Get a specific EXPLAIN plan with full output including node details and costs.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + }, + "explain_id": { + "type": "string", + "description": "EXPLAIN plan ID (from pganalyze_get_query_explains)" + } + }, + "required": [ + "database_id", + "explain_id" + ] + } + }, + { + "name": "pganalyze_get_query_explain_from_trace", + "description": "Resolve an OpenTelemetry trace span to an EXPLAIN plan. Requires OpenTelemetry integration.", + "inputSchema": { + "type": "object", + "properties": { + "span_id": { + "type": "string", + "description": "OpenTelemetry span ID" + }, + "trace_id": { + "type": "string", + "description": "OpenTelemetry trace ID" + } + }, + "required": [ + "span_id", + "trace_id" + ] + } + }, + { + "name": "pganalyze_get_backend_counts", + "description": "Get time-series connection counts by state (active, idle, waiting).", + "inputSchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "description": "The server ID" + } + }, + "required": [ + "server_id" + ] + } + }, + { + "name": "pganalyze_get_backends", + "description": "Get a point-in-time snapshot of active connections and their states.", + "inputSchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "description": "The server ID" + } + }, + "required": [ + "server_id" + ] + } + }, + { + "name": "pganalyze_get_backend_details", + "description": "Get details for a specific backend connection.", + "inputSchema": { + "type": "object", + "properties": { + "backend_id": { + "type": "string", + "description": "The backend/connection ID" + }, + "server_id": { + "type": "string", + "description": "The server ID" + } + }, + "required": [ + "backend_id", + "server_id" + ] + } + }, + { + "name": "pganalyze_get_issues", + "description": "Get active check-up issues and performance alerts. Shows slow query warnings, index problems, and health issues.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID to filter issues (optional)" + }, + "server_id": { + "type": "string", + "description": "Server ID to filter issues (optional)" + } + } + } + }, + { + "name": "pganalyze_get_checkup_status", + "description": "Get check-up status overview for a database showing passed, warning, and critical checks.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database ID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "rwx_launch_ci_run", + "description": "Launch a CI/CD pipeline run using the rwx CLI. Start here to run CI. Executes the specified workflow file (default: .rwx/ci.yml).", + "inputSchema": { + "type": "object", + "properties": { + "init": { + "type": "string", + "description": "JSON object of init parameters available in the init context (optional, e.g. {\"deploy_env\": \"staging\"})" + }, + "targets": { + "type": "string", + "description": "JSON array of specific task keys to target (optional)" + }, + "title": { + "type": "string", + "description": "Display title for the run in the RWX UI (optional)" + }, + "wait": { + "type": "string", + "description": "Wait for the run to complete before returning (true/false, default: false)" + }, + "workflow": { + "type": "string", + "description": "Path to the RWX workflow YAML file to run (default: .rwx/ci.yml, e.g. .rwx/auto-deploy.yml)" + } + } + } + }, + { + "name": "rwx_dispatch_run", + "description": "Launch a run from a pre-configured RWX dispatch workflow by key. Use instead of rwx_launch_ci_run when triggering remote/pre-configured workflows without local files.", + "inputSchema": { + "type": "object", + "properties": { + "dispatch_key": { + "type": "string", + "description": "The dispatch key identifying the pre-configured workflow" + }, + "params": { + "type": "string", + "description": "JSON object of dispatch params available in event.dispatch.params context (optional)" + }, + "ref": { + "type": "string", + "description": "Git ref (branch/tag/SHA) to use for the run (optional)" + }, + "title": { + "type": "string", + "description": "Display title for the run in the RWX UI (optional)" + }, + "wait": { + "type": "string", + "description": "Wait for the run to complete before returning (true/false, default: false)" + } + }, + "required": [ + "dispatch_key" + ] + } + }, + { + "name": "rwx_wait_for_ci_run", + "description": "Poll and wait for an RWX CI run to complete or timeout", + "inputSchema": { + "type": "object", + "properties": { + "poll_interval_seconds": { + "type": "string", + "description": "Seconds between status checks (default: 30)" + }, + "run_id": { + "type": "string", + "description": "RWX run ID or full URL to wait for" + }, + "timeout_seconds": { + "type": "string", + "description": "Maximum time to wait in seconds (default: 1800)" + } + }, + "required": [ + "run_id" + ] + } + }, + { + "name": "rwx_get_recent_runs", + "description": "Get recent CI/CD runs for a git branch. Returns all workflow runs by default; use definition_path to filter to a specific workflow.", + "inputSchema": { + "type": "object", + "properties": { + "definition_path": { + "type": "string", + "description": "Filter to a specific workflow file (e.g. .rwx/ci.yml, .rwx/auto-deploy.yml). Omit to return all workflows." + }, + "limit": { + "type": "string", + "description": "Number of runs to return (default: 5)" + }, + "ref": { + "type": "string", + "description": "Git ref (branch name) to filter runs by" + } + }, + "required": [ + "ref" + ] + } + }, + { + "name": "rwx_get_run_results", + "description": "Get structured CI/CD pipeline results including per-task pass/fail status, failed tests, and build errors. Accepts a run ID or branch/commit lookup.", + "inputSchema": { + "type": "object", + "properties": { + "branch": { + "type": "string", + "description": "Look up results by branch name instead of run ID (uses current repo unless repo is set)" + }, + "commit": { + "type": "string", + "description": "Look up results by commit SHA instead of run ID" + }, + "definition": { + "type": "string", + "description": "Definition path for branch/commit lookup (e.g. .rwx/ci.yml)" + }, + "repo": { + "type": "string", + "description": "Repository name for branch/commit lookup (default: current git repo)" + }, + "run_id": { + "type": "string", + "description": "RWX run ID or full URL (required unless branch is provided)" + }, + "task_key": { + "type": "string", + "description": "Get results for a specific task by key (e.g. ci.checks.lint) instead of the full run" + } + } + } + }, + { + "name": "rwx_get_task_logs", + "description": "Download and return full CI/CD task logs with build failure and test failure highlights", + "inputSchema": { + "type": "object", + "properties": { + "run_id": { + "type": "string", + "description": "RWX run ID — use with task_key to resolve by key instead of ID (optional)" + }, + "task_id": { + "type": "string", + "description": "RWX task ID (32-char hex) or task URL" + }, + "task_key": { + "type": "string", + "description": "Task key (e.g. ci.checks.lint) — requires run_id. Use instead of task_id." + } + } + } + }, + { + "name": "rwx_head_logs", + "description": "Return the first N lines of logs for a run or task. Supports pagination via offset.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "RWX run ID or task ID" + }, + "lines": { + "type": "string", + "description": "Number of lines to return from the beginning (default: 50, max: 50)" + }, + "offset": { + "type": "string", + "description": "Line offset to start from (default: 0). Use for pagination." + }, + "task_key": { + "type": "string", + "description": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "rwx_tail_logs", + "description": "Return the last N lines of logs for a run or task. Supports pagination via offset.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "RWX run ID or task ID" + }, + "lines": { + "type": "string", + "description": "Number of lines to return from the end (default: 50, max: 50)" + }, + "offset": { + "type": "string", + "description": "Line offset from the end (default: 0). Use for pagination to see earlier lines." + }, + "task_key": { + "type": "string", + "description": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "rwx_grep_logs", + "description": "Search CI/CD build and test logs for a pattern with context lines. Results are paginated (50 lines per page).", + "inputSchema": { + "type": "object", + "properties": { + "context": { + "type": "string", + "description": "Number of context lines before and after matches (default: 3)" + }, + "id": { + "type": "string", + "description": "RWX run ID or task ID" + }, + "page": { + "type": "string", + "description": "Page number (default: 1). Each page returns up to 50 lines of output." + }, + "pattern": { + "type": "string", + "description": "Pattern to search for in the logs (case-insensitive)" + }, + "task_key": { + "type": "string", + "description": "Task key (e.g. ci.checks.lint) — when set, id is treated as run ID and task is resolved by key" + } + }, + "required": [ + "id", + "pattern" + ] + } + }, + { + "name": "rwx_get_artifacts", + "description": "List or download artifacts for a run", + "inputSchema": { + "type": "object", + "properties": { + "artifact_key": { + "type": "string", + "description": "Specific artifact key to download (optional, downloads all if not specified)" + }, + "download": { + "type": "string", + "description": "Download artifacts (true/false, default: false — just list)" + }, + "run_id": { + "type": "string", + "description": "RWX run ID or full URL to get artifacts for" + } + }, + "required": [ + "run_id" + ] + } + }, + { + "name": "rwx_validate_workflow", + "description": "Validate an RWX workflow YAML file using the rwx CLI", + "inputSchema": { + "type": "object", + "properties": { + "file_path": { + "type": "string", + "description": "Path to the RWX workflow YAML file to validate (default: .rwx/ci.yml)" + } + } + } + }, + { + "name": "rwx_docs_search", + "description": "Search RWX documentation. Use to find docs on caching, parallelism, configuration, and other RWX features.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Maximum number of results (default: 5)" + }, + "query": { + "type": "string", + "description": "Search query (e.g. 'caching', 'parallelism', 'filtering files')" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "rwx_docs_pull", + "description": "Fetch an RWX documentation article as markdown. Use a URL from rwx_docs_search results or a docs path like /docs/caching.", + "inputSchema": { + "type": "object", + "properties": { + "url_or_path": { + "type": "string", + "description": "Full URL (https://www.rwx.com/docs/...) or path (/docs/caching) of the article to fetch" + } + }, + "required": [ + "url_or_path" + ] + } + }, + { + "name": "rwx_vaults_var_show", + "description": "Show a variable value from an RWX vault", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name to show" + }, + "vault": { + "type": "string", + "description": "Vault name (default: 'default')" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "rwx_vaults_var_set", + "description": "Set a variable in an RWX vault", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name" + }, + "value": { + "type": "string", + "description": "Variable value" + }, + "vault": { + "type": "string", + "description": "Vault name (default: 'default')" + } + }, + "required": [ + "name", + "value" + ] + } + }, + { + "name": "rwx_vaults_var_delete", + "description": "Delete a variable from an RWX vault", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Variable name to delete" + }, + "vault": { + "type": "string", + "description": "Vault name (default: 'default')" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "rwx_vaults_secret_set", + "description": "Set a secret in an RWX vault. The value is stored encrypted and cannot be read back.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Secret name" + }, + "value": { + "type": "string", + "description": "Secret value" + }, + "vault": { + "type": "string", + "description": "Vault name (default: 'default')" + } + }, + "required": [ + "name", + "value" + ] + } + }, + { + "name": "rwx_vaults_secret_delete", + "description": "Delete a secret from an RWX vault", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Secret name to delete" + }, + "vault": { + "type": "string", + "description": "Vault name (default: 'default')" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "rwx_verify_cli", + "description": "Verify the rwx CLI is installed and meets the minimum version requirement (\u003e= 3.13.0)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "ynab_get_user", + "description": "Get the authenticated user's information", + "inputSchema": { + "type": "object" + } + }, + { + "name": "ynab_list_budgets", + "description": "List all personal finance budgets the user has access to. Start here for budget, spending, and money management workflows.", + "inputSchema": { + "type": "object", + "properties": { + "include_accounts": { + "type": "string", + "description": "Include account data (true/false)" + } + } + } + }, + { + "name": "ynab_get_budget", + "description": "Get a single budget with all related entities (accounts, categories, payees, transactions, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_budget_settings", + "description": "Get budget settings including date and currency format", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_list_accounts", + "description": "List all accounts in a budget", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_account", + "description": "Get a single account by ID", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + }, + "required": [ + "account_id" + ] + } + }, + { + "name": "ynab_create_account", + "description": "Create a new account in a budget", + "inputSchema": { + "type": "object", + "properties": { + "balance": { + "type": "string", + "description": "Starting balance in milliunits (1000 = $1.00)" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "name": { + "type": "string", + "description": "Account name" + }, + "type": { + "type": "string", + "description": "Account type: checking, savings, cash, creditCard, lineOfCredit, otherAsset, otherLiability, mortgage, autoLoan, studentLoan, personalLoan, medicalDebt, otherDebt" + } + }, + "required": [ + "balance", + "name", + "type" + ] + } + }, + { + "name": "ynab_list_categories", + "description": "List all category groups and their categories for a budget", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_category", + "description": "Get a single category by ID", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + } + }, + "required": [ + "category_id" + ] + } + }, + { + "name": "ynab_create_category", + "description": "Create a new category in a category group", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_group_id": { + "type": "string", + "description": "Category group ID to add this category to" + }, + "name": { + "type": "string", + "description": "Category name" + } + }, + "required": [ + "category_group_id", + "name" + ] + } + }, + { + "name": "ynab_update_category", + "description": "Update a category's name or note", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "name": { + "type": "string", + "description": "New category name" + }, + "note": { + "type": "string", + "description": "Category note" + } + }, + "required": [ + "category_id" + ] + } + }, + { + "name": "ynab_get_month_category", + "description": "Get a category's budget amounts for a specific month", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "month": { + "type": "string", + "description": "Month in ISO date format (e.g. 2024-01-01) or 'current'" + } + }, + "required": [ + "category_id", + "month" + ] + } + }, + { + "name": "ynab_update_month_category", + "description": "Update the budgeted/assigned amount for a category in a specific month", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "budgeted": { + "type": "string", + "description": "Assigned amount in milliunits (1000 = $1.00)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "month": { + "type": "string", + "description": "Month in ISO date format or 'current'" + } + }, + "required": [ + "budgeted", + "category_id", + "month" + ] + } + }, + { + "name": "ynab_create_category_group", + "description": "Create a new category group", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "name": { + "type": "string", + "description": "Category group name (max 50 chars)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "ynab_update_category_group", + "description": "Update a category group's name", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_group_id": { + "type": "string", + "description": "Category group ID" + }, + "name": { + "type": "string", + "description": "New category group name (max 50 chars)" + } + }, + "required": [ + "category_group_id", + "name" + ] + } + }, + { + "name": "ynab_list_payees", + "description": "List all payees in a budget", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_payee", + "description": "Get a single payee by ID", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + } + }, + "required": [ + "payee_id" + ] + } + }, + { + "name": "ynab_update_payee", + "description": "Update a payee's name", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "name": { + "type": "string", + "description": "New payee name (max 500 chars)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + } + }, + "required": [ + "name", + "payee_id" + ] + } + }, + { + "name": "ynab_list_payee_locations", + "description": "List all payee locations (latitude/longitude) in a budget", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_payee_location", + "description": "Get a single payee location by ID", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "payee_location_id": { + "type": "string", + "description": "Payee location ID" + } + }, + "required": [ + "payee_location_id" + ] + } + }, + { + "name": "ynab_list_locations_for_payee", + "description": "List all locations for a specific payee", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + } + }, + "required": [ + "payee_id" + ] + } + }, + { + "name": "ynab_list_months", + "description": "List all budget months (summary of each month's budget status)", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_month", + "description": "Get a single budget month with category details", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "month": { + "type": "string", + "description": "Month in ISO date format (e.g. 2024-01-01) or 'current'" + } + }, + "required": [ + "month" + ] + } + }, + { + "name": "ynab_list_transactions", + "description": "List financial transactions (spending, expenses, purchases) for a budget, optionally filtered by date or type", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "since_date": { + "type": "string", + "description": "Only return transactions on or after this date (ISO format, e.g. 2024-01-01)" + }, + "type": { + "type": "string", + "description": "Filter: uncategorized or unapproved" + } + } + } + }, + { + "name": "ynab_get_transaction", + "description": "Get a single transaction by ID", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "transaction_id": { + "type": "string", + "description": "Transaction ID" + } + }, + "required": [ + "transaction_id" + ] + } + }, + { + "name": "ynab_list_account_transactions", + "description": "List transactions for a specific account", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "since_date": { + "type": "string", + "description": "Only return transactions on or after this date (ISO format)" + }, + "type": { + "type": "string", + "description": "Filter: uncategorized or unapproved" + } + }, + "required": [ + "account_id" + ] + } + }, + { + "name": "ynab_list_category_transactions", + "description": "List transactions for a specific category", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "since_date": { + "type": "string", + "description": "Only return transactions on or after this date (ISO format)" + }, + "type": { + "type": "string", + "description": "Filter: uncategorized or unapproved" + } + }, + "required": [ + "category_id" + ] + } + }, + { + "name": "ynab_list_payee_transactions", + "description": "List transactions for a specific payee", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + }, + "since_date": { + "type": "string", + "description": "Only return transactions on or after this date (ISO format)" + }, + "type": { + "type": "string", + "description": "Filter: uncategorized or unapproved" + } + }, + "required": [ + "payee_id" + ] + } + }, + { + "name": "ynab_list_month_transactions", + "description": "List transactions for a specific month", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "month": { + "type": "string", + "description": "Month in ISO date format (e.g. 2024-01-01) or 'current'" + }, + "since_date": { + "type": "string", + "description": "Only return transactions on or after this date (ISO format)" + }, + "type": { + "type": "string", + "description": "Filter: uncategorized or unapproved" + } + }, + "required": [ + "month" + ] + } + }, + { + "name": "ynab_create_transaction", + "description": "Create a new transaction. Amounts are in milliunits (1000 = $1.00). Outflows are negative.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID (required)" + }, + "amount": { + "type": "string", + "description": "Amount in milliunits (negative for outflows, e.g. -50000 = -$50.00)" + }, + "approved": { + "type": "string", + "description": "Whether transaction is approved (true/false)" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "cleared": { + "type": "string", + "description": "Cleared status: cleared, uncleared, or reconciled" + }, + "date": { + "type": "string", + "description": "Transaction date in ISO format (required, e.g. 2024-01-15)" + }, + "flag_color": { + "type": "string", + "description": "Flag color: red, orange, yellow, green, blue, purple" + }, + "memo": { + "type": "string", + "description": "Memo text (max 500 chars)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + }, + "payee_name": { + "type": "string", + "description": "Payee name (max 200 chars, creates new payee if payee_id not given)" + } + }, + "required": [ + "account_id", + "amount", + "date" + ] + } + }, + { + "name": "ynab_update_transaction", + "description": "Update an existing transaction", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID" + }, + "amount": { + "type": "string", + "description": "Amount in milliunits" + }, + "approved": { + "type": "string", + "description": "Whether transaction is approved (true/false)" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "cleared": { + "type": "string", + "description": "Cleared status: cleared, uncleared, or reconciled" + }, + "date": { + "type": "string", + "description": "Transaction date in ISO format" + }, + "flag_color": { + "type": "string", + "description": "Flag color: red, orange, yellow, green, blue, purple" + }, + "memo": { + "type": "string", + "description": "Memo text" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + }, + "payee_name": { + "type": "string", + "description": "Payee name" + }, + "transaction_id": { + "type": "string", + "description": "Transaction ID" + } + }, + "required": [ + "transaction_id" + ] + } + }, + { + "name": "ynab_delete_transaction", + "description": "Delete a transaction", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "transaction_id": { + "type": "string", + "description": "Transaction ID" + } + }, + "required": [ + "transaction_id" + ] + } + }, + { + "name": "ynab_list_scheduled_transactions", + "description": "List all scheduled (recurring) transactions for a budget", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + } + } + } + }, + { + "name": "ynab_get_scheduled_transaction", + "description": "Get a single scheduled transaction by ID", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "scheduled_transaction_id": { + "type": "string", + "description": "Scheduled transaction ID" + } + }, + "required": [ + "scheduled_transaction_id" + ] + } + }, + { + "name": "ynab_create_scheduled_transaction", + "description": "Create a new scheduled (recurring) transaction", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID (required)" + }, + "amount": { + "type": "string", + "description": "Amount in milliunits (negative for outflows)" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "date": { + "type": "string", + "description": "First occurrence date in ISO format (required, max 5 years in future)" + }, + "flag_color": { + "type": "string", + "description": "Flag color: red, orange, yellow, green, blue, purple" + }, + "frequency": { + "type": "string", + "description": "Recurrence: never, daily, weekly, everyOtherWeek, twiceAMonth, every4Weeks, monthly, everyOtherMonth, every3Months, every4Months, twiceAYear, yearly, everyOtherYear" + }, + "memo": { + "type": "string", + "description": "Memo text (max 500 chars)" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + }, + "payee_name": { + "type": "string", + "description": "Payee name (max 200 chars)" + } + }, + "required": [ + "account_id", + "amount", + "date", + "frequency" + ] + } + }, + { + "name": "ynab_update_scheduled_transaction", + "description": "Update an existing scheduled transaction", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID" + }, + "amount": { + "type": "string", + "description": "Amount in milliunits" + }, + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "category_id": { + "type": "string", + "description": "Category ID" + }, + "date": { + "type": "string", + "description": "First occurrence date in ISO format" + }, + "flag_color": { + "type": "string", + "description": "Flag color: red, orange, yellow, green, blue, purple" + }, + "frequency": { + "type": "string", + "description": "Recurrence frequency" + }, + "memo": { + "type": "string", + "description": "Memo text" + }, + "payee_id": { + "type": "string", + "description": "Payee ID" + }, + "payee_name": { + "type": "string", + "description": "Payee name" + }, + "scheduled_transaction_id": { + "type": "string", + "description": "Scheduled transaction ID" + } + }, + "required": [ + "scheduled_transaction_id" + ] + } + }, + { + "name": "ynab_delete_scheduled_transaction", + "description": "Delete a scheduled transaction", + "inputSchema": { + "type": "object", + "properties": { + "budget_id": { + "type": "string", + "description": "Budget ID (defaults to last-used)" + }, + "scheduled_transaction_id": { + "type": "string", + "description": "Scheduled transaction ID" + } + }, + "required": [ + "scheduled_transaction_id" + ] + } + }, + { + "name": "stripe_get_balance", + "description": "Get the current Stripe account balance (available, pending, connect_reserved). Start here to inspect funds on the Stripe account.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "stripe_list_balance_transactions", + "description": "List balance transactions (charges, refunds, payouts, fees, transfers) that affected the Stripe account balance. Use for accounting reconciliation, financial audits, fee analysis, and tracking money movement on the Stripe platform.", + "inputSchema": { + "type": "object", + "properties": { + "currency": { + "type": "string", + "description": "Three-letter ISO currency code lowercase (e.g. usd)" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "payout": { + "type": "string", + "description": "Filter to transactions paid out in this payout ID" + }, + "source": { + "type": "string", + "description": "Filter by source ID (charge, refund, etc.)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "type": { + "type": "string", + "description": "Filter by type (charge, refund, payout, transfer, adjustment, fee, etc.)" + } + } + } + }, + { + "name": "stripe_retrieve_balance_transaction", + "description": "Retrieve (get) a single balance transaction by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Balance transaction ID (e.g. txn_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_customers", + "description": "List Stripe customers. Use for browsing payers, finding accounts by email, exporting customer rosters, or paginating the customer directory.", + "inputSchema": { + "type": "object", + "properties": { + "created": { + "type": "string", + "description": "Filter by created timestamp (Unix epoch seconds) — pass a number or use nested keys gt/gte/lt/lte for ranges" + }, + "email": { + "type": "string", + "description": "Filter by exact email match" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_customer", + "description": "Retrieve (get) a single customer by ID including their default payment method, billing address, and metadata.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Customer ID (e.g. cus_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_search_customers", + "description": "Search Stripe customers using Stripe search query language (e.g. email:\"alice@example.com\" or metadata['plan']:\"pro\"). Returns matching customer records.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Number of results (1-100)" + }, + "page": { + "type": "string", + "description": "Cursor for pagination (from prior response.next_page)" + }, + "query": { + "type": "string", + "description": "Stripe search query syntax" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "stripe_create_customer", + "description": "Create a new Stripe customer record for a payer.", + "inputSchema": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "Object with line1, line2, city, state, postal_code, country" + }, + "description": { + "type": "string", + "description": "Arbitrary description for internal use" + }, + "email": { + "type": "string", + "description": "Customer email address" + }, + "metadata": { + "type": "string", + "description": "Object of key-value strings to attach (max 50 keys)" + }, + "name": { + "type": "string", + "description": "Full customer name" + }, + "phone": { + "type": "string", + "description": "Phone number" + }, + "shipping": { + "type": "string", + "description": "Object with name, phone, address" + } + } + } + }, + { + "name": "stripe_update_customer", + "description": "Update (edit) a customer's profile, contact info, default payment method, or metadata.", + "inputSchema": { + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "Billing address object" + }, + "default_source": { + "type": "string", + "description": "Default payment source ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "email": { + "type": "string", + "description": "New email" + }, + "id": { + "type": "string", + "description": "Customer ID" + }, + "invoice_settings": { + "type": "string", + "description": "Object with default_payment_method, custom_fields, footer" + }, + "metadata": { + "type": "string", + "description": "Object to merge into existing metadata (set key to empty string to clear it)" + }, + "name": { + "type": "string", + "description": "New name" + }, + "phone": { + "type": "string", + "description": "New phone" + }, + "shipping": { + "type": "string", + "description": "Shipping address object" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_delete_customer", + "description": "Delete (remove) a customer permanently. Cancels active subscriptions and disassociates payment methods.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Customer ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_charges", + "description": "List charges (card and bank transactions) processed on the Stripe account. Use for transaction history, sales reports, revenue analytics, and finding individual successful or failed payments.", + "inputSchema": { + "type": "object", + "properties": { + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "payment_intent": { + "type": "string", + "description": "Filter by payment intent ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "transfer_group": { + "type": "string", + "description": "Filter by transfer group" + } + } + } + }, + { + "name": "stripe_retrieve_charge", + "description": "Retrieve (get) a single charge by ID with full details (amount, currency, status, customer, payment_method, refunds, dispute, outcome).", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Charge ID (e.g. ch_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_search_charges", + "description": "Search charges using Stripe search query language (e.g. amount\u003e1000 AND status:\"succeeded\").", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Number of results (1-100)" + }, + "page": { + "type": "string", + "description": "Cursor for pagination" + }, + "query": { + "type": "string", + "description": "Stripe search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "stripe_capture_charge", + "description": "Capture a previously authorized but uncaptured charge (manual capture flow).", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "Optional amount to capture in smallest currency unit (cents). Defaults to full authorized amount." + }, + "id": { + "type": "string", + "description": "Charge ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_payment_intents", + "description": "List PaymentIntents — the modern payment flow object representing intent to collect from a customer. Use for monitoring in-progress, succeeded, requires_action, or failed payments.", + "inputSchema": { + "type": "object", + "properties": { + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_payment_intent", + "description": "Retrieve (get) a single PaymentIntent by ID with status, next_action, latest_charge, and client_secret.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "PaymentIntent ID (e.g. pi_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_payment_intent", + "description": "Create a new PaymentIntent to charge a customer. Amounts are in the smallest currency unit (e.g. cents for USD).", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "Amount in smallest currency unit (e.g. 1099 = $10.99)" + }, + "capture_method": { + "type": "string", + "description": "automatic or manual" + }, + "confirm": { + "type": "string", + "description": "If true, confirm the PaymentIntent in the same request (boolean)" + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency lowercase (e.g. usd)" + }, + "customer": { + "type": "string", + "description": "Customer ID to associate with this payment" + }, + "description": { + "type": "string", + "description": "Arbitrary description shown to the customer" + }, + "metadata": { + "type": "string", + "description": "Object of key-value strings" + }, + "off_session": { + "type": "string", + "description": "Boolean indicating customer is not present" + }, + "payment_method": { + "type": "string", + "description": "Payment method ID to charge" + }, + "payment_method_types": { + "type": "string", + "description": "Array of payment method types (e.g. [\"card\"])" + }, + "receipt_email": { + "type": "string", + "description": "Email address to send receipt to" + }, + "statement_descriptor": { + "type": "string", + "description": "Up to 22 chars shown on the customer's statement" + } + }, + "required": [ + "amount", + "currency" + ] + } + }, + { + "name": "stripe_update_payment_intent", + "description": "Update (edit) a PaymentIntent before it is confirmed.", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "New amount in smallest currency unit" + }, + "currency": { + "type": "string", + "description": "Currency code" + }, + "customer": { + "type": "string", + "description": "Customer ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "id": { + "type": "string", + "description": "PaymentIntent ID" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "payment_method": { + "type": "string", + "description": "Payment method ID" + }, + "receipt_email": { + "type": "string", + "description": "Receipt email address" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_confirm_payment_intent", + "description": "Confirm a PaymentIntent to attempt to collect payment.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "PaymentIntent ID" + }, + "off_session": { + "type": "string", + "description": "Boolean — confirming on behalf of an absent customer" + }, + "payment_method": { + "type": "string", + "description": "Payment method ID to attach for confirmation" + }, + "return_url": { + "type": "string", + "description": "URL to redirect after 3DS/redirect-based authentication" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_cancel_payment_intent", + "description": "Cancel a PaymentIntent that is in a cancelable state (requires_payment_method, requires_capture, requires_confirmation, requires_action, processing).", + "inputSchema": { + "type": "object", + "properties": { + "cancellation_reason": { + "type": "string", + "description": "duplicate, fraudulent, requested_by_customer, abandoned" + }, + "id": { + "type": "string", + "description": "PaymentIntent ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_search_payment_intents", + "description": "Search PaymentIntents with Stripe search query (e.g. status:\"requires_action\" AND amount\u003e5000).", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Number of results" + }, + "page": { + "type": "string", + "description": "Cursor for pagination" + }, + "query": { + "type": "string", + "description": "Stripe search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "stripe_list_refunds", + "description": "List refunds processed on the Stripe account. Use for reviewing returned money, refund audits, and tracking refund status.", + "inputSchema": { + "type": "object", + "properties": { + "charge": { + "type": "string", + "description": "Filter by charge ID" + }, + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "payment_intent": { + "type": "string", + "description": "Filter by payment intent ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_refund", + "description": "Retrieve (get) a single refund by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Refund ID (e.g. re_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_refund", + "description": "Create a refund (full or partial) for a charge or PaymentIntent. Returns money to the customer.", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "Amount to refund in smallest currency unit (defaults to full)" + }, + "charge": { + "type": "string", + "description": "Charge ID to refund (one of charge or payment_intent required)" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "payment_intent": { + "type": "string", + "description": "PaymentIntent ID to refund" + }, + "reason": { + "type": "string", + "description": "duplicate, fraudulent, or requested_by_customer" + }, + "refund_application_fee": { + "type": "string", + "description": "Boolean — whether to refund the application fee" + }, + "reverse_transfer": { + "type": "string", + "description": "Boolean — reverse the transfer to a connected account" + } + } + } + }, + { + "name": "stripe_update_refund", + "description": "Update (edit) a refund's metadata.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Refund ID" + }, + "metadata": { + "type": "string", + "description": "Metadata object to merge" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_disputes", + "description": "List chargeback disputes filed against charges on the Stripe account. Use for fraud monitoring, chargeback response workflows, and dispute analytics.", + "inputSchema": { + "type": "object", + "properties": { + "charge": { + "type": "string", + "description": "Filter by charge ID" + }, + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "payment_intent": { + "type": "string", + "description": "Filter by payment intent ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_dispute", + "description": "Retrieve (get) a single dispute by ID including evidence and status.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Dispute ID (e.g. dp_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_update_dispute", + "description": "Update (edit) a dispute to submit evidence for chargeback response.", + "inputSchema": { + "type": "object", + "properties": { + "evidence": { + "type": "string", + "description": "Evidence object (e.g. customer_communication, receipt, service_documentation, shipping_documentation, etc.)" + }, + "id": { + "type": "string", + "description": "Dispute ID" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "submit": { + "type": "string", + "description": "Boolean — submit evidence immediately" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_payouts", + "description": "List payouts (transfers from the Stripe balance to a bank account). Use for cash-out tracking, settlement reconciliation, and finance reports.", + "inputSchema": { + "type": "object", + "properties": { + "arrival_date": { + "type": "string", + "description": "Filter by arrival_date timestamp" + }, + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "destination": { + "type": "string", + "description": "Filter by bank account or card destination ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "status": { + "type": "string", + "description": "Filter by status (paid, pending, in_transit, canceled, failed)" + } + } + } + }, + { + "name": "stripe_retrieve_payout", + "description": "Retrieve (get) a single payout by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Payout ID (e.g. po_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_payout", + "description": "Create a manual payout from the Stripe balance to the default bank account.", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "Amount in smallest currency unit" + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency lowercase" + }, + "description": { + "type": "string", + "description": "Description" + }, + "destination": { + "type": "string", + "description": "Bank account or debit card ID (optional override)" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "method": { + "type": "string", + "description": "standard or instant" + } + }, + "required": [ + "amount", + "currency" + ] + } + }, + { + "name": "stripe_list_subscriptions", + "description": "List recurring billing subscriptions. Use for MRR/ARR reports, churn analysis, active customer counts, and finding subscriptions in trial, past_due, or canceled state.", + "inputSchema": { + "type": "object", + "properties": { + "collection_method": { + "type": "string", + "description": "charge_automatically or send_invoice" + }, + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "price": { + "type": "string", + "description": "Filter by price ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "status": { + "type": "string", + "description": "Filter by status (active, past_due, unpaid, canceled, incomplete, incomplete_expired, trialing, all)" + } + } + } + }, + { + "name": "stripe_retrieve_subscription", + "description": "Retrieve (get) a single subscription by ID including items, current period, and billing cycle.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Subscription ID (e.g. sub_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_search_subscriptions", + "description": "Search subscriptions using Stripe search query (e.g. status:\"trialing\" AND created\u003e1700000000).", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Number of results" + }, + "page": { + "type": "string", + "description": "Cursor for pagination" + }, + "query": { + "type": "string", + "description": "Stripe search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "stripe_create_subscription", + "description": "Create a new subscription that recurringly bills a customer for one or more prices.", + "inputSchema": { + "type": "object", + "properties": { + "collection_method": { + "type": "string", + "description": "charge_automatically or send_invoice" + }, + "coupon": { + "type": "string", + "description": "Coupon ID to apply" + }, + "customer": { + "type": "string", + "description": "Customer ID to subscribe (required)" + }, + "days_until_due": { + "type": "string", + "description": "Days until invoice is due (only for send_invoice)" + }, + "default_payment_method": { + "type": "string", + "description": "Payment method ID to use for invoices" + }, + "items": { + "type": "string", + "description": "Array of subscription items, each with a 'price' ID and optional 'quantity'" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "trial_end": { + "type": "string", + "description": "Unix timestamp when trial ends (or 'now')" + }, + "trial_period_days": { + "type": "string", + "description": "Number of days for free trial" + } + }, + "required": [ + "customer", + "items" + ] + } + }, + { + "name": "stripe_update_subscription", + "description": "Update (edit) a subscription's items, prices, quantities, billing settings, or metadata.", + "inputSchema": { + "type": "object", + "properties": { + "cancel_at_period_end": { + "type": "string", + "description": "Boolean — cancel at end of current period" + }, + "default_payment_method": { + "type": "string", + "description": "Payment method ID" + }, + "id": { + "type": "string", + "description": "Subscription ID" + }, + "items": { + "type": "string", + "description": "Updated items array — each item may include 'id' (existing item), 'price', 'quantity', or 'deleted':true" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "pause_collection": { + "type": "string", + "description": "Object with behavior (keep_as_draft, mark_uncollectible, void) and resumes_at" + }, + "proration_behavior": { + "type": "string", + "description": "create_prorations, none, or always_invoice" + }, + "trial_end": { + "type": "string", + "description": "Unix timestamp or 'now'" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_cancel_subscription", + "description": "Cancel a subscription immediately (or at period end via stripe_update_subscription with cancel_at_period_end).", + "inputSchema": { + "type": "object", + "properties": { + "cancellation_details": { + "type": "string", + "description": "Object with comment and feedback" + }, + "id": { + "type": "string", + "description": "Subscription ID" + }, + "invoice_now": { + "type": "string", + "description": "Boolean — generate a final invoice for unbilled usage" + }, + "prorate": { + "type": "string", + "description": "Boolean — credit prorated unused time" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_subscription_items", + "description": "List items (line items) belonging to a subscription.", + "inputSchema": { + "type": "object", + "properties": { + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "subscription": { + "type": "string", + "description": "Subscription ID" + } + }, + "required": [ + "subscription" + ] + } + }, + { + "name": "stripe_retrieve_subscription_item", + "description": "Retrieve (get) a single subscription item by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Subscription item ID (e.g. si_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_products", + "description": "List products (goods or services sold on Stripe). Use for product catalog browsing and finding SKUs.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Filter by active (true/false)" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "ids": { + "type": "string", + "description": "Comma-separated product IDs" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "shippable": { + "type": "string", + "description": "Filter shippable goods (true/false)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "url": { + "type": "string", + "description": "Filter by URL" + } + } + } + }, + { + "name": "stripe_retrieve_product", + "description": "Retrieve (get) a single product by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Product ID (e.g. prod_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_product", + "description": "Create a new product to sell.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Boolean — whether the product can be used" + }, + "default_price_data": { + "type": "string", + "description": "Object describing the default price (currency, unit_amount, recurring)" + }, + "description": { + "type": "string", + "description": "Product description" + }, + "images": { + "type": "string", + "description": "Array of image URLs" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "name": { + "type": "string", + "description": "Product name (required)" + }, + "shippable": { + "type": "string", + "description": "Boolean — whether the product is shippable" + }, + "tax_code": { + "type": "string", + "description": "Tax code ID" + }, + "url": { + "type": "string", + "description": "Product URL" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "stripe_update_product", + "description": "Update (edit) a product's name, description, images, or metadata.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Boolean" + }, + "default_price": { + "type": "string", + "description": "Default price ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "id": { + "type": "string", + "description": "Product ID" + }, + "images": { + "type": "string", + "description": "Array of image URLs" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "name": { + "type": "string", + "description": "New name" + }, + "url": { + "type": "string", + "description": "Product URL" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_delete_product", + "description": "Delete (remove) a product. Only succeeds if no prices reference it.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Product ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_prices", + "description": "List prices attached to products. Each price defines how much and how often to charge.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Filter by active" + }, + "currency": { + "type": "string", + "description": "Filter by currency" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "product": { + "type": "string", + "description": "Filter by product ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "type": { + "type": "string", + "description": "one_time or recurring" + } + } + } + }, + { + "name": "stripe_retrieve_price", + "description": "Retrieve (get) a single price by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Price ID (e.g. price_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_price", + "description": "Create a new price for a product (one-time or recurring).", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Boolean" + }, + "billing_scheme": { + "type": "string", + "description": "per_unit or tiered" + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency lowercase (required)" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "nickname": { + "type": "string", + "description": "Internal display name" + }, + "product": { + "type": "string", + "description": "Product ID (required if product_data not given)" + }, + "product_data": { + "type": "string", + "description": "Inline product object with name to create alongside the price" + }, + "recurring": { + "type": "string", + "description": "Object with interval (day/week/month/year) and interval_count" + }, + "tiers": { + "type": "string", + "description": "Array of tier objects (for tiered pricing)" + }, + "tiers_mode": { + "type": "string", + "description": "graduated or volume" + }, + "unit_amount": { + "type": "string", + "description": "Price in smallest currency unit (required for fixed pricing)" + } + }, + "required": [ + "currency" + ] + } + }, + { + "name": "stripe_update_price", + "description": "Update (edit) a price's nickname, active flag, or metadata. Note: most fields are immutable on existing prices.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Boolean" + }, + "id": { + "type": "string", + "description": "Price ID" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "nickname": { + "type": "string", + "description": "Nickname" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_invoices", + "description": "List invoices on the Stripe account. Use for billing audits, finding past_due/open/paid invoices, and revenue reporting.", + "inputSchema": { + "type": "object", + "properties": { + "collection_method": { + "type": "string", + "description": "charge_automatically or send_invoice" + }, + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "due_date": { + "type": "string", + "description": "Filter by due_date timestamp" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "status": { + "type": "string", + "description": "draft, open, paid, uncollectible, void" + }, + "subscription": { + "type": "string", + "description": "Filter by subscription ID" + } + } + } + }, + { + "name": "stripe_retrieve_invoice", + "description": "Retrieve (get) a single invoice by ID with line items, totals, and status.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Invoice ID (e.g. in_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_retrieve_upcoming_invoice", + "description": "Retrieve (get) the upcoming (preview) invoice for a customer or subscription before it is finalized. Useful for previewing the next bill.", + "inputSchema": { + "type": "object", + "properties": { + "coupon": { + "type": "string", + "description": "Coupon ID for preview" + }, + "customer": { + "type": "string", + "description": "Customer ID" + }, + "subscription": { + "type": "string", + "description": "Subscription ID" + } + } + } + }, + { + "name": "stripe_search_invoices", + "description": "Search invoices with Stripe search query (e.g. status:\"open\" AND total\u003e10000).", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Number of results" + }, + "page": { + "type": "string", + "description": "Cursor for pagination" + }, + "query": { + "type": "string", + "description": "Stripe search query" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "stripe_create_invoice", + "description": "Create a draft invoice for a customer. Add invoice items separately or use auto_advance to finalize automatically.", + "inputSchema": { + "type": "object", + "properties": { + "auto_advance": { + "type": "string", + "description": "Boolean — finalize and attempt collection automatically" + }, + "collection_method": { + "type": "string", + "description": "charge_automatically or send_invoice" + }, + "customer": { + "type": "string", + "description": "Customer ID (required)" + }, + "days_until_due": { + "type": "string", + "description": "Days until due (for send_invoice)" + }, + "description": { + "type": "string", + "description": "Description" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "subscription": { + "type": "string", + "description": "Subscription ID to associate" + } + }, + "required": [ + "customer" + ] + } + }, + { + "name": "stripe_finalize_invoice", + "description": "Finalize a draft invoice — locks line items and generates the final amount.", + "inputSchema": { + "type": "object", + "properties": { + "auto_advance": { + "type": "string", + "description": "Boolean — proceed with payment collection" + }, + "id": { + "type": "string", + "description": "Invoice ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_pay_invoice", + "description": "Pay an open invoice using a payment method or by charging the customer's default source.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Invoice ID" + }, + "off_session": { + "type": "string", + "description": "Boolean" + }, + "paid_out_of_band": { + "type": "string", + "description": "Boolean — mark as paid externally without collecting funds" + }, + "payment_method": { + "type": "string", + "description": "Payment method ID to charge" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_send_invoice", + "description": "Email an open invoice to the customer.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Invoice ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_void_invoice", + "description": "Void an open invoice. Cannot be undone.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Invoice ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_delete_invoice", + "description": "Delete (remove) a draft invoice permanently. Only allowed for draft invoices.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Invoice ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_invoice_items", + "description": "List pending or attached invoice items (line items added to a customer's next invoice).", + "inputSchema": { + "type": "object", + "properties": { + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "invoice": { + "type": "string", + "description": "Filter by invoice ID" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "pending": { + "type": "string", + "description": "Only return pending items (true/false)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_create_invoice_item", + "description": "Create an invoice item that will be added to a customer's next invoice or attached to a specific invoice.", + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "description": "Amount in smallest currency unit" + }, + "currency": { + "type": "string", + "description": "Currency code" + }, + "customer": { + "type": "string", + "description": "Customer ID (required)" + }, + "description": { + "type": "string", + "description": "Line item description" + }, + "invoice": { + "type": "string", + "description": "Optional invoice ID to attach to" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "price": { + "type": "string", + "description": "Price ID (alternative to amount+currency)" + }, + "quantity": { + "type": "string", + "description": "Quantity (defaults to 1)" + }, + "subscription": { + "type": "string", + "description": "Subscription ID to attach to" + } + }, + "required": [ + "customer" + ] + } + }, + { + "name": "stripe_list_payment_methods", + "description": "List payment methods attached to a customer (cards, bank accounts, wallets).", + "inputSchema": { + "type": "object", + "properties": { + "customer": { + "type": "string", + "description": "Customer ID (required)" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "type": { + "type": "string", + "description": "card, us_bank_account, sepa_debit, etc." + } + }, + "required": [ + "customer" + ] + } + }, + { + "name": "stripe_retrieve_payment_method", + "description": "Retrieve (get) a single payment method by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Payment method ID (e.g. pm_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_attach_payment_method", + "description": "Attach a payment method to a customer for later reuse.", + "inputSchema": { + "type": "object", + "properties": { + "customer": { + "type": "string", + "description": "Customer ID to attach to" + }, + "id": { + "type": "string", + "description": "Payment method ID" + } + }, + "required": [ + "customer", + "id" + ] + } + }, + { + "name": "stripe_detach_payment_method", + "description": "Detach (remove) a payment method from its customer.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Payment method ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_setup_intents", + "description": "List SetupIntents (objects representing intent to save a payment method for future use).", + "inputSchema": { + "type": "object", + "properties": { + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "payment_method": { + "type": "string", + "description": "Filter by payment method ID" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_setup_intent", + "description": "Retrieve (get) a single SetupIntent by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "SetupIntent ID (e.g. seti_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_setup_intent", + "description": "Create a SetupIntent to collect a customer's payment method for future use.", + "inputSchema": { + "type": "object", + "properties": { + "confirm": { + "type": "string", + "description": "Boolean — confirm immediately" + }, + "customer": { + "type": "string", + "description": "Customer ID" + }, + "description": { + "type": "string", + "description": "Description" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "payment_method": { + "type": "string", + "description": "Payment method ID" + }, + "payment_method_types": { + "type": "string", + "description": "Array of allowed payment method types" + }, + "usage": { + "type": "string", + "description": "on_session or off_session" + } + } + } + }, + { + "name": "stripe_list_coupons", + "description": "List discount coupons defined on the Stripe account.", + "inputSchema": { + "type": "object", + "properties": { + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_retrieve_coupon", + "description": "Retrieve (get) a single coupon by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Coupon ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_create_coupon", + "description": "Create a discount coupon that can be applied to customers, invoices, or subscriptions.", + "inputSchema": { + "type": "object", + "properties": { + "amount_off": { + "type": "string", + "description": "Fixed amount discount in smallest currency unit" + }, + "currency": { + "type": "string", + "description": "Required when amount_off is set" + }, + "duration": { + "type": "string", + "description": "once, repeating, or forever" + }, + "duration_in_months": { + "type": "string", + "description": "Number of months (for duration=repeating)" + }, + "id": { + "type": "string", + "description": "Optional coupon ID (auto-generated if omitted)" + }, + "max_redemptions": { + "type": "string", + "description": "Maximum total redemptions" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + }, + "name": { + "type": "string", + "description": "Display name shown on receipts/invoices" + }, + "percent_off": { + "type": "string", + "description": "Percentage discount (0-100). Use this or amount_off." + }, + "redeem_by": { + "type": "string", + "description": "Unix timestamp coupon expires for new redemptions" + } + }, + "required": [ + "duration" + ] + } + }, + { + "name": "stripe_delete_coupon", + "description": "Delete (remove) a coupon.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Coupon ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "stripe_list_promotion_codes", + "description": "List customer-facing promotion codes (the code strings tied to a coupon).", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Filter by active" + }, + "code": { + "type": "string", + "description": "Filter by code string" + }, + "coupon": { + "type": "string", + "description": "Filter by coupon ID" + }, + "customer": { + "type": "string", + "description": "Filter by customer ID" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + } + } + } + }, + { + "name": "stripe_create_promotion_code", + "description": "Create a customer-facing promotion code tied to a coupon.", + "inputSchema": { + "type": "object", + "properties": { + "active": { + "type": "string", + "description": "Boolean" + }, + "code": { + "type": "string", + "description": "Customer-facing code (auto-generated if omitted)" + }, + "coupon": { + "type": "string", + "description": "Coupon ID (required)" + }, + "customer": { + "type": "string", + "description": "Restrict to a specific customer ID" + }, + "expires_at": { + "type": "string", + "description": "Unix timestamp" + }, + "max_redemptions": { + "type": "string", + "description": "Maximum total redemptions" + }, + "metadata": { + "type": "string", + "description": "Metadata object" + } + }, + "required": [ + "coupon" + ] + } + }, + { + "name": "stripe_list_events", + "description": "List Stripe events (webhook event log). Use for auditing webhook history, replaying missed events, or finding when a specific object changed.", + "inputSchema": { + "type": "object", + "properties": { + "created": { + "type": "string", + "description": "Filter by creation timestamp" + }, + "delivery_success": { + "type": "string", + "description": "Boolean — filter by webhook delivery outcome" + }, + "ending_before": { + "type": "string", + "description": "Cursor for pagination — an object ID for the previous page" + }, + "limit": { + "type": "string", + "description": "Number of objects to return (1-100, default 10)" + }, + "starting_after": { + "type": "string", + "description": "Cursor for pagination — an object ID for the next page" + }, + "type": { + "type": "string", + "description": "Filter by event type (e.g. charge.succeeded, invoice.paid)" + }, + "types": { + "type": "string", + "description": "Array of event types" + } + } + } + }, + { + "name": "stripe_retrieve_event", + "description": "Retrieve (get) a single event by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Event ID (e.g. evt_...)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "amazon_search_products", + "description": "Search for products on Amazon. Returns up to 20 results with title, price, rating, Prime eligibility, and ASIN. Start here for product discovery workflows.", + "inputSchema": { + "type": "object", + "properties": { + "search_term": { + "type": "string", + "description": "Search query (e.g. 'wireless headphones', 'collagen powder')" + } + }, + "required": [ + "search_term" + ] + } + }, + { + "name": "amazon_get_product", + "description": "Get detailed product information by ASIN (Amazon Standard Identification Number, 10 characters). Returns title, price, description sections, reviews, and image URL. Use after amazon_search_products to drill into a specific product.", + "inputSchema": { + "type": "object", + "properties": { + "asin": { + "type": "string", + "description": "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')" + } + }, + "required": [ + "asin" + ] + } + }, + { + "name": "amazon_get_orders", + "description": "Get the authenticated user's recent order history. Returns order details including items, delivery address, status, and return eligibility. Requires valid session cookies.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "amazon_get_cart", + "description": "Get the current Amazon cart contents. Returns items with title, price, quantity, availability, and cart subtotal. Requires valid session cookies.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "amazon_add_to_cart", + "description": "Add a product to the Amazon cart by ASIN. Navigates to product page and submits the add-to-cart form. Requires valid session cookies.", + "inputSchema": { + "type": "object", + "properties": { + "asin": { + "type": "string", + "description": "Product ASIN — exactly 10 characters (e.g. 'B0CHXKM5GK')" + } + }, + "required": [ + "asin" + ] + } + }, + { + "name": "amazon_clear_cart", + "description": "Remove all items from the Amazon cart. Iterates through cart items and deletes each one. Requires valid session cookies.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gmail_get_profile", + "description": "Get the current user's Gmail profile (email, messages total, threads total, history ID). Start here to verify access.", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me' for authenticated user)" + } + } + } + }, + { + "name": "gmail_list_messages", + "description": "List email messages in the user's inbox. Search and find mail using Gmail query syntax.", + "inputSchema": { + "type": "object", + "properties": { + "include_spam_trash": { + "type": "string", + "description": "Include SPAM and TRASH (true/false)" + }, + "label_ids": { + "type": "string", + "description": "Comma-separated label IDs to filter by" + }, + "max_results": { + "type": "string", + "description": "Max results per page (default 10, max 500)" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "q": { + "type": "string", + "description": "Gmail search query (same as Gmail search box)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_message", + "description": "Get a specific email message by ID. Read the full mail content, headers, and attachments.", + "inputSchema": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "Format: full, metadata, minimal, raw (default full)" + }, + "message_id": { + "type": "string", + "description": "Message ID" + }, + "metadata_headers": { + "type": "string", + "description": "Comma-separated headers to include when format=metadata" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_id" + ] + } + }, + { + "name": "gmail_send_message", + "description": "Send an email message. Provide raw RFC 2822 formatted message or use to/subject/body for simple messages", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Email body (plain text)" + }, + "from": { + "type": "string", + "description": "Sender email address (defaults to authenticated user)" + }, + "raw": { + "type": "string", + "description": "Base64url-encoded RFC 2822 message (overrides from/to/subject/body)" + }, + "subject": { + "type": "string", + "description": "Email subject" + }, + "thread_id": { + "type": "string", + "description": "Thread ID to reply to" + }, + "to": { + "type": "string", + "description": "Recipient email address(es), comma-separated" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_delete_message", + "description": "Permanently delete a message (not trash). Cannot be undone", + "inputSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "Message ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_id" + ] + } + }, + { + "name": "gmail_trash_message", + "description": "Move a message to the trash", + "inputSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "Message ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_id" + ] + } + }, + { + "name": "gmail_untrash_message", + "description": "Remove a message from the trash", + "inputSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "Message ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_id" + ] + } + }, + { + "name": "gmail_modify_message", + "description": "Modify labels on a message (add and/or remove labels)", + "inputSchema": { + "type": "object", + "properties": { + "add_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to add" + }, + "message_id": { + "type": "string", + "description": "Message ID" + }, + "remove_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to remove" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_id" + ] + } + }, + { + "name": "gmail_batch_modify", + "description": "Modify labels on multiple messages at once", + "inputSchema": { + "type": "object", + "properties": { + "add_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to add" + }, + "message_ids": { + "type": "string", + "description": "Comma-separated message IDs" + }, + "remove_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to remove" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_ids" + ] + } + }, + { + "name": "gmail_batch_delete", + "description": "Permanently delete multiple messages. Cannot be undone", + "inputSchema": { + "type": "object", + "properties": { + "message_ids": { + "type": "string", + "description": "Comma-separated message IDs" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "message_ids" + ] + } + }, + { + "name": "gmail_get_attachment", + "description": "Get a message attachment by ID", + "inputSchema": { + "type": "object", + "properties": { + "attachment_id": { + "type": "string", + "description": "Attachment ID" + }, + "message_id": { + "type": "string", + "description": "Message ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "attachment_id", + "message_id" + ] + } + }, + { + "name": "gmail_list_threads", + "description": "List threads in the user's mailbox", + "inputSchema": { + "type": "object", + "properties": { + "include_spam_trash": { + "type": "string", + "description": "Include SPAM and TRASH (true/false)" + }, + "label_ids": { + "type": "string", + "description": "Comma-separated label IDs to filter by" + }, + "max_results": { + "type": "string", + "description": "Max results per page (default 10, max 500)" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "q": { + "type": "string", + "description": "Gmail search query" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_thread", + "description": "Get a specific thread with all its messages", + "inputSchema": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "Format: full, metadata, minimal (default full)" + }, + "metadata_headers": { + "type": "string", + "description": "Comma-separated headers to include when format=metadata" + }, + "thread_id": { + "type": "string", + "description": "Thread ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "thread_id" + ] + } + }, + { + "name": "gmail_delete_thread", + "description": "Permanently delete a thread (not trash). Cannot be undone", + "inputSchema": { + "type": "object", + "properties": { + "thread_id": { + "type": "string", + "description": "Thread ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "thread_id" + ] + } + }, + { + "name": "gmail_trash_thread", + "description": "Move a thread to the trash", + "inputSchema": { + "type": "object", + "properties": { + "thread_id": { + "type": "string", + "description": "Thread ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "thread_id" + ] + } + }, + { + "name": "gmail_untrash_thread", + "description": "Remove a thread from the trash", + "inputSchema": { + "type": "object", + "properties": { + "thread_id": { + "type": "string", + "description": "Thread ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "thread_id" + ] + } + }, + { + "name": "gmail_modify_thread", + "description": "Modify labels on a thread (add and/or remove labels)", + "inputSchema": { + "type": "object", + "properties": { + "add_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to add" + }, + "remove_label_ids": { + "type": "string", + "description": "Comma-separated label IDs to remove" + }, + "thread_id": { + "type": "string", + "description": "Thread ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "thread_id" + ] + } + }, + { + "name": "gmail_list_labels", + "description": "List all labels in the user's mailbox", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_label", + "description": "Get a specific label by ID", + "inputSchema": { + "type": "object", + "properties": { + "label_id": { + "type": "string", + "description": "Label ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "label_id" + ] + } + }, + { + "name": "gmail_create_label", + "description": "Create a new label", + "inputSchema": { + "type": "object", + "properties": { + "background_color": { + "type": "string", + "description": "Background color hex code" + }, + "label_list_visibility": { + "type": "string", + "description": "Visibility in label list: labelShow, labelShowIfUnread, labelHide" + }, + "message_list_visibility": { + "type": "string", + "description": "Visibility in message list: show, hide" + }, + "name": { + "type": "string", + "description": "Label name" + }, + "text_color": { + "type": "string", + "description": "Text color hex code" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gmail_update_label", + "description": "Update a label", + "inputSchema": { + "type": "object", + "properties": { + "background_color": { + "type": "string", + "description": "Background color hex code" + }, + "label_id": { + "type": "string", + "description": "Label ID" + }, + "label_list_visibility": { + "type": "string", + "description": "Visibility in label list: labelShow, labelShowIfUnread, labelHide" + }, + "message_list_visibility": { + "type": "string", + "description": "Visibility in message list: show, hide" + }, + "name": { + "type": "string", + "description": "New label name" + }, + "text_color": { + "type": "string", + "description": "Text color hex code" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "label_id" + ] + } + }, + { + "name": "gmail_delete_label", + "description": "Permanently delete a label", + "inputSchema": { + "type": "object", + "properties": { + "label_id": { + "type": "string", + "description": "Label ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "label_id" + ] + } + }, + { + "name": "gmail_list_drafts", + "description": "List email drafts in the user's mailbox. View unsent composed messages.", + "inputSchema": { + "type": "object", + "properties": { + "include_spam_trash": { + "type": "string", + "description": "Include SPAM and TRASH (true/false)" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "q": { + "type": "string", + "description": "Gmail search query" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_draft", + "description": "Get a specific draft by ID", + "inputSchema": { + "type": "object", + "properties": { + "draft_id": { + "type": "string", + "description": "Draft ID" + }, + "format": { + "type": "string", + "description": "Format: full, metadata, minimal, raw (default full)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "draft_id" + ] + } + }, + { + "name": "gmail_create_draft", + "description": "Create a new email draft. Compose and write a message to send later or save as a reply draft.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Email body (plain text)" + }, + "raw": { + "type": "string", + "description": "Base64url-encoded RFC 2822 message (overrides to/subject/body)" + }, + "subject": { + "type": "string", + "description": "Email subject" + }, + "thread_id": { + "type": "string", + "description": "Thread ID for reply drafts" + }, + "to": { + "type": "string", + "description": "Recipient email address(es), comma-separated" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_draft", + "description": "Update an existing email draft. Edit the composed mail message before sending.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Email body (plain text)" + }, + "draft_id": { + "type": "string", + "description": "Draft ID" + }, + "raw": { + "type": "string", + "description": "Base64url-encoded RFC 2822 message (overrides to/subject/body)" + }, + "subject": { + "type": "string", + "description": "Email subject" + }, + "thread_id": { + "type": "string", + "description": "Thread ID for reply drafts" + }, + "to": { + "type": "string", + "description": "Recipient email address(es), comma-separated" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "draft_id" + ] + } + }, + { + "name": "gmail_delete_draft", + "description": "Permanently delete a draft", + "inputSchema": { + "type": "object", + "properties": { + "draft_id": { + "type": "string", + "description": "Draft ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "draft_id" + ] + } + }, + { + "name": "gmail_send_draft", + "description": "Send an existing email draft. Deliver a previously composed mail message.", + "inputSchema": { + "type": "object", + "properties": { + "draft_id": { + "type": "string", + "description": "Draft ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "draft_id" + ] + } + }, + { + "name": "gmail_list_history", + "description": "List the history of changes to the mailbox since a given history ID", + "inputSchema": { + "type": "object", + "properties": { + "history_types": { + "type": "string", + "description": "Comma-separated types: messageAdded, messageDeleted, labelAdded, labelRemoved" + }, + "label_id": { + "type": "string", + "description": "Filter by label ID" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "start_history_id": { + "type": "string", + "description": "History ID to start listing from (required)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "start_history_id" + ] + } + }, + { + "name": "gmail_get_vacation", + "description": "Get vacation responder settings", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_vacation", + "description": "Update vacation responder settings", + "inputSchema": { + "type": "object", + "properties": { + "enable_auto_reply": { + "type": "string", + "description": "Enable auto-reply (true/false)" + }, + "end_time": { + "type": "string", + "description": "End time in milliseconds since epoch" + }, + "response_body_html": { + "type": "string", + "description": "Auto-reply body (HTML)" + }, + "response_body_plain_text": { + "type": "string", + "description": "Auto-reply body (plain text)" + }, + "response_subject": { + "type": "string", + "description": "Auto-reply subject" + }, + "restrict_to_contacts": { + "type": "string", + "description": "Only reply to contacts (true/false)" + }, + "restrict_to_domain": { + "type": "string", + "description": "Only reply to same domain (true/false)" + }, + "start_time": { + "type": "string", + "description": "Start time in milliseconds since epoch" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_auto_forwarding", + "description": "Get auto-forwarding settings", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_auto_forwarding", + "description": "Update auto-forwarding settings", + "inputSchema": { + "type": "object", + "properties": { + "disposition": { + "type": "string", + "description": "What to do with forwarded messages: leaveInInbox, archive, trash, markRead" + }, + "email_address": { + "type": "string", + "description": "Email address to forward to" + }, + "enabled": { + "type": "string", + "description": "Enable auto-forwarding (true/false)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_imap", + "description": "Get IMAP settings", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_imap", + "description": "Update IMAP settings", + "inputSchema": { + "type": "object", + "properties": { + "auto_expunge": { + "type": "string", + "description": "Auto-expunge (true/false)" + }, + "enabled": { + "type": "string", + "description": "Enable IMAP (true/false)" + }, + "expunge_behavior": { + "type": "string", + "description": "Expunge behavior: archive, deleteForever, trash" + }, + "max_folder_size": { + "type": "string", + "description": "Max folder size (0 for no limit)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_pop", + "description": "Get POP settings", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_pop", + "description": "Update POP settings", + "inputSchema": { + "type": "object", + "properties": { + "access_window": { + "type": "string", + "description": "Access window: disabled, allMail, fromNowOn" + }, + "disposition": { + "type": "string", + "description": "What to do after POP fetch: leaveInInbox, archive, trash, markRead" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_language", + "description": "Get language settings", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_update_language", + "description": "Update language settings", + "inputSchema": { + "type": "object", + "properties": { + "display_language": { + "type": "string", + "description": "Display language code (e.g. en, fr, de)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "display_language" + ] + } + }, + { + "name": "gmail_list_filters", + "description": "List all message filters", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_filter", + "description": "Get a specific message filter", + "inputSchema": { + "type": "object", + "properties": { + "filter_id": { + "type": "string", + "description": "Filter ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "filter_id" + ] + } + }, + { + "name": "gmail_create_filter", + "description": "Create a new message filter", + "inputSchema": { + "type": "object", + "properties": { + "action": { + "type": "string", + "description": "JSON string of filter action (addLabelIds, removeLabelIds, forward, sizeComparison)" + }, + "criteria": { + "type": "string", + "description": "JSON string of filter criteria (from, to, subject, query, negatedQuery, hasAttachment, excludeChats, size, sizeComparison)" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "action", + "criteria" + ] + } + }, + { + "name": "gmail_delete_filter", + "description": "Delete a message filter", + "inputSchema": { + "type": "object", + "properties": { + "filter_id": { + "type": "string", + "description": "Filter ID" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "filter_id" + ] + } + }, + { + "name": "gmail_list_forwarding_addresses", + "description": "List all forwarding addresses", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_forwarding_address", + "description": "Get a specific forwarding address", + "inputSchema": { + "type": "object", + "properties": { + "forwarding_email": { + "type": "string", + "description": "Forwarding email address" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "forwarding_email" + ] + } + }, + { + "name": "gmail_create_forwarding_address", + "description": "Create a forwarding address (requires verification)", + "inputSchema": { + "type": "object", + "properties": { + "forwarding_email": { + "type": "string", + "description": "Email address to add as forwarding address" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "forwarding_email" + ] + } + }, + { + "name": "gmail_delete_forwarding_address", + "description": "Delete a forwarding address", + "inputSchema": { + "type": "object", + "properties": { + "forwarding_email": { + "type": "string", + "description": "Forwarding email address to remove" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "forwarding_email" + ] + } + }, + { + "name": "gmail_list_send_as", + "description": "List send-as aliases", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_send_as", + "description": "Get a specific send-as alias", + "inputSchema": { + "type": "object", + "properties": { + "send_as_email": { + "type": "string", + "description": "Send-as email address" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "send_as_email" + ] + } + }, + { + "name": "gmail_create_send_as", + "description": "Create a custom 'from' send-as alias", + "inputSchema": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "description": "Display name for the alias" + }, + "is_default": { + "type": "string", + "description": "Set as default send-as (true/false)" + }, + "reply_to_address": { + "type": "string", + "description": "Reply-to address" + }, + "send_as_email": { + "type": "string", + "description": "Email address for the alias" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "send_as_email" + ] + } + }, + { + "name": "gmail_update_send_as", + "description": "Update a send-as alias", + "inputSchema": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "description": "Display name" + }, + "is_default": { + "type": "string", + "description": "Set as default (true/false)" + }, + "reply_to_address": { + "type": "string", + "description": "Reply-to address" + }, + "send_as_email": { + "type": "string", + "description": "Send-as email address" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "send_as_email" + ] + } + }, + { + "name": "gmail_delete_send_as", + "description": "Delete a send-as alias", + "inputSchema": { + "type": "object", + "properties": { + "send_as_email": { + "type": "string", + "description": "Send-as email address to delete" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "send_as_email" + ] + } + }, + { + "name": "gmail_verify_send_as", + "description": "Send a verification email to a send-as alias address", + "inputSchema": { + "type": "object", + "properties": { + "send_as_email": { + "type": "string", + "description": "Send-as email address to verify" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "send_as_email" + ] + } + }, + { + "name": "gmail_list_delegates", + "description": "List delegates for the account", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + } + } + }, + { + "name": "gmail_get_delegate", + "description": "Get a specific delegate", + "inputSchema": { + "type": "object", + "properties": { + "delegate_email": { + "type": "string", + "description": "Delegate email address" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "delegate_email" + ] + } + }, + { + "name": "gmail_create_delegate", + "description": "Add a delegate with verification status set to accepted", + "inputSchema": { + "type": "object", + "properties": { + "delegate_email": { + "type": "string", + "description": "Email address of the delegate" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "delegate_email" + ] + } + }, + { + "name": "gmail_delete_delegate", + "description": "Remove a delegate", + "inputSchema": { + "type": "object", + "properties": { + "delegate_email": { + "type": "string", + "description": "Delegate email address to remove" + }, + "user_id": { + "type": "string", + "description": "User ID (defaults to 'me')" + } + }, + "required": [ + "delegate_email" + ] + } + }, + { + "name": "jira_search_issues", + "description": "Search issues using JQL (Jira Query Language). Start here for most issue workflows. Returns paginated results; pass nextPageToken from response to fetch next page", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Comma-separated fields to return (default: summary,status,assignee,priority,issuetype)" + }, + "jql": { + "type": "string", + "description": "JQL query (e.g., 'project = PROJ AND status = Open')" + }, + "max_results": { + "type": "string", + "description": "Max results per page (default 200, server may cap)" + }, + "next_page_token": { + "type": "string", + "description": "Cursor for next page (from previous response's nextPageToken field)" + } + }, + "required": [ + "jql" + ] + } + }, + { + "name": "jira_get_issue", + "description": "Get full details of a specific issue by key or ID", + "inputSchema": { + "type": "object", + "properties": { + "expand": { + "type": "string", + "description": "Comma-separated expansions (e.g., changelog,renderedFields)" + }, + "fields": { + "type": "string", + "description": "Comma-separated fields to return (default: all)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123) or ID" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_create_issue", + "description": "Create a new issue. Use jira_list_issue_types to find valid issue type names. Supports custom fields — use jira_list_fields to discover field IDs", + "inputSchema": { + "type": "object", + "properties": { + "assignee_id": { + "type": "string", + "description": "Account ID of assignee (use jira_search_users to find)" + }, + "custom_fields": { + "type": "string", + "description": "JSON object of custom field values keyed by field ID (e.g. {\"customfield_10001\": \"value\", \"customfield_10002\": {\"id\": \"10100\"}}). Use jira_list_fields to discover field IDs and types" + }, + "description": { + "type": "string", + "description": "Issue description (plain text, converted to ADF)" + }, + "issue_type": { + "type": "string", + "description": "Issue type name (e.g., Bug, Task, Story)" + }, + "labels": { + "type": "string", + "description": "Comma-separated labels" + }, + "parent_key": { + "type": "string", + "description": "Parent issue key for subtasks (e.g., PROJ-100)" + }, + "priority": { + "type": "string", + "description": "Priority name (e.g., High, Medium, Low)" + }, + "project_key": { + "type": "string", + "description": "Project key (e.g., PROJ)" + }, + "summary": { + "type": "string", + "description": "Issue summary/title" + } + }, + "required": [ + "issue_type", + "project_key", + "summary" + ] + } + }, + { + "name": "jira_update_issue", + "description": "Update an existing issue's fields including custom fields — use jira_list_fields to discover custom field IDs", + "inputSchema": { + "type": "object", + "properties": { + "assignee_id": { + "type": "string", + "description": "Account ID of assignee (empty string to unassign)" + }, + "custom_fields": { + "type": "string", + "description": "JSON object of custom field values keyed by field ID (e.g. {\"customfield_10001\": \"value\", \"customfield_10002\": {\"id\": \"10100\"}}). Use jira_list_fields to discover field IDs and types" + }, + "description": { + "type": "string", + "description": "New description (plain text, converted to ADF)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + }, + "labels": { + "type": "string", + "description": "Comma-separated labels (replaces existing)" + }, + "priority": { + "type": "string", + "description": "Priority name" + }, + "summary": { + "type": "string", + "description": "New summary" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_delete_issue", + "description": "Delete an issue", + "inputSchema": { + "type": "object", + "properties": { + "delete_subtasks": { + "type": "string", + "description": "Also delete subtasks (true/false, default false)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_transition_issue", + "description": "Transition an issue to a new status. Use jira_get_transitions first to find valid transition IDs", + "inputSchema": { + "type": "object", + "properties": { + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + }, + "transition_id": { + "type": "string", + "description": "Transition ID (use jira_get_transitions to find valid IDs)" + } + }, + "required": [ + "issue_key", + "transition_id" + ] + } + }, + { + "name": "jira_get_transitions", + "description": "List available transitions for an issue. Use before jira_transition_issue to find valid transition IDs", + "inputSchema": { + "type": "object", + "properties": { + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_assign_issue", + "description": "Assign an issue to a user", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account ID of assignee (use jira_search_users to find, or empty/-1 to unassign)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "account_id", + "issue_key" + ] + } + }, + { + "name": "jira_list_comments", + "description": "List comments on an issue", + "inputSchema": { + "type": "object", + "properties": { + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_add_comment", + "description": "Add a comment to an issue", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Comment body (plain text, converted to ADF)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "body", + "issue_key" + ] + } + }, + { + "name": "jira_update_comment", + "description": "Update an existing comment", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "New comment body (plain text, converted to ADF)" + }, + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "body", + "comment_id", + "issue_key" + ] + } + }, + { + "name": "jira_delete_comment", + "description": "Delete a comment from an issue", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "comment_id", + "issue_key" + ] + } + }, + { + "name": "jira_list_issue_links", + "description": "List links on an issue (blocks, is blocked by, duplicates, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_create_issue_link", + "description": "Create a link between two issues", + "inputSchema": { + "type": "object", + "properties": { + "inward_issue": { + "type": "string", + "description": "Issue key for the inward side" + }, + "outward_issue": { + "type": "string", + "description": "Issue key for the outward side" + }, + "type_name": { + "type": "string", + "description": "Link type name (e.g., Blocks, Duplicate, Cloners)" + } + }, + "required": [ + "inward_issue", + "outward_issue", + "type_name" + ] + } + }, + { + "name": "jira_delete_issue_link", + "description": "Delete an issue link by link ID", + "inputSchema": { + "type": "object", + "properties": { + "link_id": { + "type": "string", + "description": "Issue link ID" + } + }, + "required": [ + "link_id" + ] + } + }, + { + "name": "jira_list_projects", + "description": "List all accessible projects", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Max results per page (default 50)" + }, + "query": { + "type": "string", + "description": "Filter projects by name" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + } + } + }, + { + "name": "jira_get_project", + "description": "Get details of a specific project", + "inputSchema": { + "type": "object", + "properties": { + "project_key": { + "type": "string", + "description": "Project key or ID" + } + }, + "required": [ + "project_key" + ] + } + }, + { + "name": "jira_list_project_components", + "description": "List components in a project", + "inputSchema": { + "type": "object", + "properties": { + "project_key": { + "type": "string", + "description": "Project key or ID" + } + }, + "required": [ + "project_key" + ] + } + }, + { + "name": "jira_list_project_versions", + "description": "List versions (releases) in a project", + "inputSchema": { + "type": "object", + "properties": { + "project_key": { + "type": "string", + "description": "Project key or ID" + } + }, + "required": [ + "project_key" + ] + } + }, + { + "name": "jira_list_project_statuses", + "description": "List valid statuses for a project's issue types", + "inputSchema": { + "type": "object", + "properties": { + "project_key": { + "type": "string", + "description": "Project key or ID" + } + }, + "required": [ + "project_key" + ] + } + }, + { + "name": "jira_list_boards", + "description": "List all agile boards", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Max results per page (default 50)" + }, + "project_key": { + "type": "string", + "description": "Filter by project key" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + }, + "type": { + "type": "string", + "description": "Board type: scrum, kanban, simple" + } + } + } + }, + { + "name": "jira_get_board", + "description": "Get details of an agile board", + "inputSchema": { + "type": "object", + "properties": { + "board_id": { + "type": "string", + "description": "Board ID" + } + }, + "required": [ + "board_id" + ] + } + }, + { + "name": "jira_list_sprints", + "description": "List sprints for a board", + "inputSchema": { + "type": "object", + "properties": { + "board_id": { + "type": "string", + "description": "Board ID" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + }, + "state": { + "type": "string", + "description": "Filter by state: active, future, closed" + } + }, + "required": [ + "board_id" + ] + } + }, + { + "name": "jira_get_sprint", + "description": "Get details of a sprint", + "inputSchema": { + "type": "object", + "properties": { + "sprint_id": { + "type": "string", + "description": "Sprint ID" + } + }, + "required": [ + "sprint_id" + ] + } + }, + { + "name": "jira_create_sprint", + "description": "Create a new sprint", + "inputSchema": { + "type": "object", + "properties": { + "board_id": { + "type": "string", + "description": "Board ID (origin board)" + }, + "end_date": { + "type": "string", + "description": "End date (ISO 8601)" + }, + "goal": { + "type": "string", + "description": "Sprint goal" + }, + "name": { + "type": "string", + "description": "Sprint name" + }, + "start_date": { + "type": "string", + "description": "Start date (ISO 8601)" + } + }, + "required": [ + "board_id", + "name" + ] + } + }, + { + "name": "jira_update_sprint", + "description": "Update a sprint's details", + "inputSchema": { + "type": "object", + "properties": { + "end_date": { + "type": "string", + "description": "End date (ISO 8601)" + }, + "goal": { + "type": "string", + "description": "Sprint goal" + }, + "name": { + "type": "string", + "description": "Sprint name" + }, + "sprint_id": { + "type": "string", + "description": "Sprint ID" + }, + "start_date": { + "type": "string", + "description": "Start date (ISO 8601)" + }, + "state": { + "type": "string", + "description": "Sprint state: active, future, closed" + } + }, + "required": [ + "sprint_id" + ] + } + }, + { + "name": "jira_get_sprint_issues", + "description": "List issues in a sprint", + "inputSchema": { + "type": "object", + "properties": { + "jql": { + "type": "string", + "description": "Additional JQL filter" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "sprint_id": { + "type": "string", + "description": "Sprint ID" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + }, + "required": [ + "sprint_id" + ] + } + }, + { + "name": "jira_move_issues_to_sprint", + "description": "Move issues to a sprint", + "inputSchema": { + "type": "object", + "properties": { + "issues": { + "type": "string", + "description": "Comma-separated issue keys (e.g., PROJ-1,PROJ-2)" + }, + "sprint_id": { + "type": "string", + "description": "Sprint ID" + } + }, + "required": [ + "issues", + "sprint_id" + ] + } + }, + { + "name": "jira_list_board_backlog", + "description": "List issues in the backlog of a board", + "inputSchema": { + "type": "object", + "properties": { + "board_id": { + "type": "string", + "description": "Board ID" + }, + "jql": { + "type": "string", + "description": "Additional JQL filter" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + }, + "required": [ + "board_id" + ] + } + }, + { + "name": "jira_get_board_config", + "description": "Get configuration of an agile board (columns, estimation, ranking)", + "inputSchema": { + "type": "object", + "properties": { + "board_id": { + "type": "string", + "description": "Board ID" + } + }, + "required": [ + "board_id" + ] + } + }, + { + "name": "jira_get_myself", + "description": "Get the current authenticated user's details", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_search_users", + "description": "Search for users by name or email. Use to find account IDs for assignment", + "inputSchema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Search query (name or email)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "jira_get_user", + "description": "Get details of a specific user by account ID", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "User account ID" + } + }, + "required": [ + "account_id" + ] + } + }, + { + "name": "jira_list_issue_types", + "description": "List all issue types available in the Jira instance", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_list_priorities", + "description": "List all issue priorities", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_list_statuses", + "description": "List all issue statuses", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_list_labels", + "description": "List all labels used across the instance", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_list_fields", + "description": "List all fields (system and custom) available in the instance", + "inputSchema": { + "type": "object" + } + }, + { + "name": "jira_list_filters", + "description": "Search saved filters", + "inputSchema": { + "type": "object", + "properties": { + "filter_name": { + "type": "string", + "description": "Filter by name (substring match)" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + } + } + }, + { + "name": "jira_get_filter", + "description": "Get details of a saved filter including its JQL", + "inputSchema": { + "type": "object", + "properties": { + "filter_id": { + "type": "string", + "description": "Filter ID" + } + }, + "required": [ + "filter_id" + ] + } + }, + { + "name": "jira_list_worklogs", + "description": "List work logs for an issue", + "inputSchema": { + "type": "object", + "properties": { + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "start_at": { + "type": "string", + "description": "Pagination offset" + } + }, + "required": [ + "issue_key" + ] + } + }, + { + "name": "jira_add_worklog", + "description": "Add a work log entry to an issue", + "inputSchema": { + "type": "object", + "properties": { + "comment": { + "type": "string", + "description": "Work log comment (plain text)" + }, + "issue_key": { + "type": "string", + "description": "Issue key (e.g., PROJ-123)" + }, + "started": { + "type": "string", + "description": "When work started (ISO 8601, defaults to now)" + }, + "time_spent": { + "type": "string", + "description": "Time spent (e.g., 2h, 30m, 1d)" + } + }, + "required": [ + "issue_key", + "time_spent" + ] + } + }, + { + "name": "jira_get_server_info", + "description": "Get Jira instance server info (version, deployment type, URLs)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "confluence_list_spaces", + "description": "List all accessible Confluence spaces. Start here to find space IDs for other operations", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 250)" + }, + "status": { + "type": "string", + "description": "Filter by status: current, archived" + }, + "type": { + "type": "string", + "description": "Filter by type: global, personal" + } + } + } + }, + { + "name": "confluence_get_space", + "description": "Get details of a specific space by ID", + "inputSchema": { + "type": "object", + "properties": { + "space_id": { + "type": "string", + "description": "Space ID" + } + }, + "required": [ + "space_id" + ] + } + }, + { + "name": "confluence_search", + "description": "Search Confluence content using CQL (Confluence Query Language). Supports pages, blog posts, comments, and attachments", + "inputSchema": { + "type": "object", + "properties": { + "cql": { + "type": "string", + "description": "CQL query (e.g., 'type=page AND space=DEV AND title~\"design doc\"')" + }, + "excerpt": { + "type": "string", + "description": "Include excerpt in results: none, highlight, indexed (default none)" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 100)" + }, + "start": { + "type": "string", + "description": "Pagination offset (0-based)" + } + }, + "required": [ + "cql" + ] + } + }, + { + "name": "confluence_list_pages", + "description": "List pages, optionally filtered by space or title", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 250)" + }, + "sort": { + "type": "string", + "description": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + }, + "space_id": { + "type": "string", + "description": "Filter by space ID" + }, + "status": { + "type": "string", + "description": "Filter by status: current, trashed, draft (default current)" + }, + "title": { + "type": "string", + "description": "Filter by exact title" + } + } + } + }, + { + "name": "confluence_get_page", + "description": "Get full details of a specific page by ID, including body content", + "inputSchema": { + "type": "object", + "properties": { + "body_format": { + "type": "string", + "description": "Body format to return: storage, atlas_doc_format, view (default storage)" + }, + "page_id": { + "type": "string", + "description": "Page ID" + }, + "version": { + "type": "string", + "description": "Specific version number to retrieve" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "confluence_create_page", + "description": "Create a new page in a space", + "inputSchema": { + "type": "object", + "properties": { + "body_format": { + "type": "string", + "description": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + }, + "body_value": { + "type": "string", + "description": "Page body content" + }, + "parent_id": { + "type": "string", + "description": "Parent page ID for nesting" + }, + "space_id": { + "type": "string", + "description": "Space ID to create page in" + }, + "status": { + "type": "string", + "description": "Page status: current, draft (default current)" + }, + "title": { + "type": "string", + "description": "Page title" + } + }, + "required": [ + "body_value", + "space_id", + "title" + ] + } + }, + { + "name": "confluence_update_page", + "description": "Update an existing page. Requires the current version number (use confluence_get_page to find it)", + "inputSchema": { + "type": "object", + "properties": { + "body_format": { + "type": "string", + "description": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + }, + "body_value": { + "type": "string", + "description": "New page body content" + }, + "page_id": { + "type": "string", + "description": "Page ID" + }, + "status": { + "type": "string", + "description": "Page status: current, draft" + }, + "title": { + "type": "string", + "description": "New page title" + }, + "version_message": { + "type": "string", + "description": "Version change message" + }, + "version_number": { + "type": "string", + "description": "New version number (must be current version + 1)" + } + }, + "required": [ + "body_value", + "page_id", + "title", + "version_number" + ] + } + }, + { + "name": "confluence_delete_page", + "description": "Delete a page by ID", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "Page ID" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "confluence_get_page_children", + "description": "Get child pages of a specific page", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 250)" + }, + "page_id": { + "type": "string", + "description": "Parent page ID" + }, + "sort": { + "type": "string", + "description": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "confluence_list_blog_posts", + "description": "List blog posts, optionally filtered by space or title", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 250)" + }, + "sort": { + "type": "string", + "description": "Sort order: id, -id, title, -title, created-date, -created-date, modified-date, -modified-date" + }, + "space_id": { + "type": "string", + "description": "Filter by space ID" + }, + "status": { + "type": "string", + "description": "Filter by status: current, trashed, draft (default current)" + }, + "title": { + "type": "string", + "description": "Filter by exact title" + } + } + } + }, + { + "name": "confluence_get_blog_post", + "description": "Get full details of a specific blog post by ID, including body content", + "inputSchema": { + "type": "object", + "properties": { + "blogpost_id": { + "type": "string", + "description": "Blog post ID" + }, + "body_format": { + "type": "string", + "description": "Body format to return: storage, atlas_doc_format, view (default storage)" + }, + "version": { + "type": "string", + "description": "Specific version number to retrieve" + } + }, + "required": [ + "blogpost_id" + ] + } + }, + { + "name": "confluence_create_blog_post", + "description": "Create a new blog post in a space", + "inputSchema": { + "type": "object", + "properties": { + "body_format": { + "type": "string", + "description": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + }, + "body_value": { + "type": "string", + "description": "Blog post body content" + }, + "space_id": { + "type": "string", + "description": "Space ID to create blog post in" + }, + "status": { + "type": "string", + "description": "Blog post status: current, draft (default current)" + }, + "title": { + "type": "string", + "description": "Blog post title" + } + }, + "required": [ + "body_value", + "space_id", + "title" + ] + } + }, + { + "name": "confluence_update_blog_post", + "description": "Update an existing blog post. Requires the current version number (use confluence_get_blog_post to find it)", + "inputSchema": { + "type": "object", + "properties": { + "blogpost_id": { + "type": "string", + "description": "Blog post ID" + }, + "body_format": { + "type": "string", + "description": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + }, + "body_value": { + "type": "string", + "description": "New blog post body content" + }, + "status": { + "type": "string", + "description": "Blog post status: current, draft" + }, + "title": { + "type": "string", + "description": "New blog post title" + }, + "version_message": { + "type": "string", + "description": "Version change message" + }, + "version_number": { + "type": "string", + "description": "New version number (must be current version + 1)" + } + }, + "required": [ + "blogpost_id", + "body_value", + "title", + "version_number" + ] + } + }, + { + "name": "confluence_delete_blog_post", + "description": "Delete a blog post by ID", + "inputSchema": { + "type": "object", + "properties": { + "blogpost_id": { + "type": "string", + "description": "Blog post ID" + } + }, + "required": [ + "blogpost_id" + ] + } + }, + { + "name": "confluence_list_comments", + "description": "List footer comments on a page or blog post", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Max results per page (default 25, max 250)" + }, + "parent_id": { + "type": "string", + "description": "Parent content ID" + }, + "parent_type": { + "type": "string", + "description": "Parent content type: page, blogpost" + } + }, + "required": [ + "parent_id", + "parent_type" + ] + } + }, + { + "name": "confluence_create_comment", + "description": "Add a footer comment to a page or blog post", + "inputSchema": { + "type": "object", + "properties": { + "body_format": { + "type": "string", + "description": "Body format: storage (XHTML), atlas_doc_format (ADF JSON) (default storage)" + }, + "body_value": { + "type": "string", + "description": "Comment body content" + }, + "parent_id": { + "type": "string", + "description": "Parent content ID" + }, + "parent_type": { + "type": "string", + "description": "Parent content type: page, blogpost" + } + }, + "required": [ + "body_value", + "parent_id", + "parent_type" + ] + } + }, + { + "name": "confluence_delete_comment", + "description": "Delete a footer comment by ID", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + } + }, + "required": [ + "comment_id" + ] + } + }, + { + "name": "notion_create_database", + "description": "Create a new database under a parent page", + "inputSchema": { + "type": "object", + "properties": { + "is_inline": { + "type": "string", + "description": "Set to true to create an inline database (default false)" + }, + "parent": { + "type": "string", + "description": "Parent object with page_id (e.g. {\"page_id\": \"...\"})" + }, + "properties": { + "type": "string", + "description": "Property schema object defining columns and their types" + }, + "title": { + "type": "string", + "description": "Title of the database (rich text array)" + } + }, + "required": [ + "parent" + ] + } + }, + { + "name": "notion_retrieve_data_source", + "description": "Retrieve a data source's property schema. Use before query_data_source to understand available columns, types, and filter options.", + "inputSchema": { + "type": "object", + "properties": { + "data_source_id": { + "type": "string", + "description": "Block ID of the data source (the id field from search results — NOT collection_id)" + } + }, + "required": [ + "data_source_id" + ] + } + }, + { + "name": "notion_update_data_source", + "description": "Update a data source's title or property schema", + "inputSchema": { + "type": "object", + "properties": { + "data_source_id": { + "type": "string", + "description": "Block ID of the data source (the id field from search results — NOT collection_id)" + }, + "properties": { + "type": "string", + "description": "Updated property schema object" + }, + "title": { + "type": "string", + "description": "New title (rich text array)" + } + }, + "required": [ + "data_source_id" + ] + } + }, + { + "name": "notion_query_data_source", + "description": "Query a data source (database) with optional filters and sorts, returning paginated rows. Use retrieve_data_source first to see the schema.", + "inputSchema": { + "type": "object", + "properties": { + "data_source_id": { + "type": "string", + "description": "Block ID of the data source (the id field from search results — NOT collection_id). The handler resolves the collection internally." + }, + "filter": { + "type": "string", + "description": "Filter object to narrow results. Use retrieve_data_source to see available property names and types for building filters." + }, + "page_size": { + "type": "string", + "description": "Number of results per page (max 100)" + }, + "sorts": { + "type": "string", + "description": "Array of sort objects (property + direction)" + }, + "start_cursor": { + "type": "string", + "description": "Cursor for pagination" + } + }, + "required": [ + "data_source_id" + ] + } + }, + { + "name": "notion_list_data_source_templates", + "description": "List available templates for a data source", + "inputSchema": { + "type": "object", + "properties": { + "data_source_id": { + "type": "string", + "description": "Block ID of the data source (the id field from search results — NOT collection_id)" + } + }, + "required": [ + "data_source_id" + ] + } + }, + { + "name": "notion_retrieve_database", + "description": "Retrieve a database by block ID. Equivalent to retrieve_data_source — both accept the block ID.", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "ID of the database" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "notion_create_page", + "description": "Create a new page or database row with properties only (no content blocks). page_id parent creates a subpage; database_id parent creates a row. For pages with content, prefer create_page_with_content.", + "inputSchema": { + "type": "object", + "properties": { + "parent": { + "type": "string", + "description": "Parent: {\"page_id\": \"...\"} for subpage, or {\"database_id\": \"\u003ccollection_id\u003e\"} for database row. Use collection_id from search results, NOT the search result id field" + }, + "properties": { + "type": "string", + "description": "Page property values object" + }, + "title": { + "type": "string", + "description": "Page title (convenience — sets the title property)" + } + }, + "required": [ + "parent" + ] + } + }, + { + "name": "notion_retrieve_page", + "description": "Retrieve a page's metadata and properties only. For full page content, prefer get_page_content.", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "ID of the page" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "notion_update_page", + "description": "Update a page's property values (status, assignee, dates, etc). Does not modify page content blocks — use append_block_children or update_block for that.", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Set to true to archive the page" + }, + "page_id": { + "type": "string", + "description": "ID of the page to update" + }, + "properties": { + "type": "string", + "description": "Updated property values object" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "notion_move_page", + "description": "Move a page to a new parent page or database", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "ID of the page to move" + }, + "parent": { + "type": "string", + "description": "New parent object with page_id or database_id" + } + }, + "required": [ + "page_id", + "parent" + ] + } + }, + { + "name": "notion_retrieve_page_property", + "description": "Retrieve a single property value. Rarely needed — retrieve_page returns all properties at once.", + "inputSchema": { + "type": "object", + "properties": { + "page_id": { + "type": "string", + "description": "ID of the page" + }, + "property_id": { + "type": "string", + "description": "ID or name of the property to retrieve" + } + }, + "required": [ + "page_id", + "property_id" + ] + } + }, + { + "name": "notion_retrieve_block", + "description": "Retrieve a single block by ID. For full page content, prefer get_page_content.", + "inputSchema": { + "type": "object", + "properties": { + "block_id": { + "type": "string", + "description": "ID of the block" + } + }, + "required": [ + "block_id" + ] + } + }, + { + "name": "notion_update_block", + "description": "Update a block's content", + "inputSchema": { + "type": "object", + "properties": { + "archived": { + "type": "string", + "description": "Set to true to archive the block" + }, + "block_id": { + "type": "string", + "description": "ID of the block to update" + }, + "type_content": { + "type": "string", + "description": "Block type-specific content object" + } + }, + "required": [ + "block_id" + ] + } + }, + { + "name": "notion_delete_block", + "description": "Delete a block by ID (marks as not alive)", + "inputSchema": { + "type": "object", + "properties": { + "block_id": { + "type": "string", + "description": "ID of the block to delete" + } + }, + "required": [ + "block_id" + ] + } + }, + { + "name": "notion_get_block_children", + "description": "List immediate child blocks of a block. For full page tree, prefer get_page_content.", + "inputSchema": { + "type": "object", + "properties": { + "block_id": { + "type": "string", + "description": "ID of the parent block" + } + }, + "required": [ + "block_id" + ] + } + }, + { + "name": "notion_append_block_children", + "description": "Append new child blocks to a page or block. Use for adding content to existing pages.", + "inputSchema": { + "type": "object", + "properties": { + "block_id": { + "type": "string", + "description": "ID of the parent block" + }, + "children": { + "type": "string", + "description": "Array of v3 block objects: {\"type\": \"text\", \"properties\": {\"title\": [[\"content\"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {\"code_language\": \"Python\"}), divider, toggle" + } + }, + "required": [ + "block_id", + "children" + ] + } + }, + { + "name": "notion_search", + "description": "Search across all pages and data sources in the workspace. Start here for most workflows. For database results: use id (block ID) for retrieve_data_source and query_data_source; use collection_id for creating rows via create_page with database_id parent.", + "inputSchema": { + "type": "object", + "properties": { + "filters": { + "type": "string", + "description": "Additional filter object for v3 search" + }, + "limit": { + "type": "string", + "description": "Maximum number of results (default 20)" + }, + "query": { + "type": "string", + "description": "Search query text. Searches page titles and content." + }, + "sort": { + "type": "string", + "description": "Sort object with field and direction" + }, + "space_id": { + "type": "string", + "description": "Space ID (auto-filled if not provided)" + }, + "type": { + "type": "string", + "description": "Filter by type: \"page\" or \"data_source\"" + } + } + } + }, + { + "name": "notion_list_users", + "description": "List all users in the workspace", + "inputSchema": { + "type": "object" + } + }, + { + "name": "notion_retrieve_user", + "description": "Retrieve a user by ID", + "inputSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "description": "ID of the user" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "notion_get_self", + "description": "Retrieve the current authenticated user's ID and settings", + "inputSchema": { + "type": "object" + } + }, + { + "name": "notion_create_comment", + "description": "Create a comment on a page or in an existing discussion thread. Provide page_id for a new discussion, or discussion_id to reply to an existing thread.", + "inputSchema": { + "type": "object", + "properties": { + "discussion_id": { + "type": "string", + "description": "ID of an existing discussion thread (from retrieve_comments). Omit for new discussions — use page_id instead." + }, + "page_id": { + "type": "string", + "description": "ID of the page (required for new discussion threads, omit when replying via discussion_id)" + }, + "text": { + "type": "string", + "description": "Plain text content of the comment" + } + }, + "required": [ + "text" + ] + } + }, + { + "name": "notion_retrieve_comments", + "description": "Retrieve all comment threads on a page. Returns discussions with their comments.", + "inputSchema": { + "type": "object", + "properties": { + "block_id": { + "type": "string", + "description": "ID of the block or page" + } + }, + "required": [ + "block_id" + ] + } + }, + { + "name": "notion_get_page_content", + "description": "Retrieve a page and all its block content in one call. Preferred over retrieve_page — returns the full page tree, not just metadata.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Maximum number of blocks to load (default 100)" + }, + "page_id": { + "type": "string", + "description": "ID of the page (from search results or a known page URL)" + } + }, + "required": [ + "page_id" + ] + } + }, + { + "name": "notion_create_page_with_content", + "description": "Create a page or database row with properties and block content in a single atomic transaction. Preferred over create_page + append_block_children — fewer calls, atomic. page_id parent creates a subpage; database_id parent creates a row.", + "inputSchema": { + "type": "object", + "properties": { + "children": { + "type": "string", + "description": "Array of v3 block objects: {\"type\": \"text\", \"properties\": {\"title\": [[\"content\"]]}}. Types: text, header, sub_header, sub_sub_header, bulleted_list (unordered), numbered_list (ordered, auto-numbered — do not add manual number/letter prefixes), to_do, quote, callout, code (set language via format: {\"code_language\": \"Python\"}), divider, toggle" + }, + "parent": { + "type": "string", + "description": "Parent: {\"page_id\": \"...\"} for subpage, or {\"database_id\": \"\u003ccollection_id\u003e\"} for database row. Use collection_id from search results, NOT the search result id field" + }, + "properties": { + "type": "string", + "description": "Page property values object" + }, + "title": { + "type": "string", + "description": "Page title (convenience)" + } + }, + "required": [ + "children", + "parent" + ] + } + }, + { + "name": "ollama_list_models", + "description": "List all locally installed Ollama AI models with sizes, families, parameter counts, and quantization levels. Start here to discover available models before running chat, generate, or embed.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "ollama_show_model", + "description": "Show details of a specific Ollama model including parameters, prompt template, capabilities (vision, tools, thinking), and license. Returns rendered markdown. Use after list_models to inspect a model before using it.", + "inputSchema": { + "type": "object", + "properties": { + "model": { + "type": "string", + "description": "Model name (e.g. 'gemma3', 'llama3.2:7b'). Use list_models to discover available names." + } + }, + "required": [ + "model" + ] + } + }, + { + "name": "ollama_pull_model", + "description": "Download and install an AI model from the Ollama model registry. May take minutes for large models — do not retry on timeout. Use model names like 'gemma3', 'llama3.2', 'qwen3'. Use list_models after to confirm the download completed.", + "inputSchema": { + "type": "object", + "properties": { + "model": { + "type": "string", + "description": "Model name to download from registry (e.g. 'gemma3', 'llama3.2:7b', 'qwen3:14b')" + } + }, + "required": [ + "model" + ] + } + }, + { + "name": "ollama_delete_model", + "description": "Permanently delete a locally installed Ollama model. This is irreversible — the model must be re-downloaded with pull_model to restore it. Use list_models first to verify the exact model name.", + "inputSchema": { + "type": "object", + "properties": { + "model": { + "type": "string", + "description": "Exact model name to delete. Must match a name from list_models." + } + }, + "required": [ + "model" + ] + } + }, + { + "name": "ollama_copy_model", + "description": "Create a copy of an existing local Ollama model with a new name. Use before create_model to preserve the original when customizing.", + "inputSchema": { + "type": "object", + "properties": { + "destination": { + "type": "string", + "description": "New model name to create" + }, + "source": { + "type": "string", + "description": "Existing model name to copy from (must be installed locally)" + } + }, + "required": [ + "destination", + "source" + ] + } + }, + { + "name": "ollama_create_model", + "description": "Create a custom Ollama model from an existing base model. Embed a system prompt, override the prompt template, or change quantization. May take minutes for re-quantization — do not retry on timeout. Use show_model after to verify the result.", + "inputSchema": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Base model to derive from (e.g. 'gemma3'). Must be installed locally." + }, + "model": { + "type": "string", + "description": "Name for the new custom model" + }, + "quantize": { + "type": "string", + "description": "Quantization level to apply (e.g. 'q4_K_M', 'q8_0'). Re-quantizes the model weights." + }, + "system": { + "type": "string", + "description": "System prompt to embed permanently in the model" + }, + "template": { + "type": "string", + "description": "Go template string for prompt formatting" + } + }, + "required": [ + "model" + ] + } + }, + { + "name": "ollama_list_running", + "description": "List currently loaded and running Ollama models with VRAM usage, context window size, and automatic unload time. Use to check GPU memory pressure before loading additional models.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "ollama_get_version", + "description": "Get the Ollama server version. Use to verify the server is reachable and check compatibility.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "ollama_chat", + "description": "Send a multi-turn chat conversation to a local Ollama model and get a complete response. Preferred over generate for conversations — maintains message history with roles. Supports tool calling, structured JSON output, vision (images in messages), and thinking/reasoning mode. Returns the full response in one call (non-streaming).", + "inputSchema": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output" + }, + "keep_alive": { + "type": "string", + "description": "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes." + }, + "messages": { + "type": "string", + "description": "Array of message objects. Each has 'role' ('system', 'user', 'assistant') and 'content' (string). For vision: add 'images' array with base64-encoded strings." + }, + "model": { + "type": "string", + "description": "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check." + }, + "options": { + "type": "string", + "description": "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc." + }, + "think": { + "type": "string", + "description": "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled." + }, + "tools": { + "type": "string", + "description": "Array of tool/function definitions the model may call (OpenAI function-calling format)" + } + }, + "required": [ + "messages", + "model" + ] + } + }, + { + "name": "ollama_generate", + "description": "Generate text completion from a single prompt using a local Ollama model. Use for one-shot generation without conversation history — prefer chat for multi-turn dialogue. Supports vision (base64 images), fill-in-the-middle (prompt + suffix), structured JSON output, and thinking mode. Returns the full response in one call (non-streaming).", + "inputSchema": { + "type": "object", + "properties": { + "format": { + "type": "string", + "description": "Constrain output format: 'json' for freeform JSON, or a JSON schema object for structured output" + }, + "images": { + "type": "string", + "description": "Array of base64-encoded images for multimodal/vision models (e.g. llava, gemma4)" + }, + "keep_alive": { + "type": "string", + "description": "Duration to keep model in memory after request: '5m', '1h', or 0 to unload immediately. Default: 5 minutes." + }, + "model": { + "type": "string", + "description": "Model name (e.g. 'gemma3', 'llama3.2'). Must be installed — use list_models to check." + }, + "options": { + "type": "string", + "description": "Model parameters object: temperature (0-2), top_k, top_p, seed, num_ctx (context window), num_predict (max tokens), etc." + }, + "prompt": { + "type": "string", + "description": "Text prompt for generation" + }, + "raw": { + "type": "string", + "description": "If true, prompt is sent directly without applying the model's chat template. Use for pre-formatted prompts only." + }, + "suffix": { + "type": "string", + "description": "Text after the insertion point for fill-in-the-middle completion. Model generates text between prompt and suffix." + }, + "system": { + "type": "string", + "description": "System prompt to prepend. Overrides any system prompt embedded in the model." + }, + "think": { + "type": "string", + "description": "Enable chain-of-thought reasoning: true, false, 'high', 'medium', or 'low'. Response includes a 'thinking' field when enabled." + } + }, + "required": [ + "model" + ] + } + }, + { + "name": "ollama_embed", + "description": "Generate vector embeddings for text using a local Ollama embedding model. For semantic search, similarity matching, and RAG pipelines. Supports single string or batch (array of strings) input. Not all models support embeddings — use an embedding model like 'all-minilm' or 'nomic-embed-text'. Returns 400 error if the model lacks embedding support.", + "inputSchema": { + "type": "object", + "properties": { + "dimensions": { + "type": "string", + "description": "Custom output vector dimensions. Only supported by some models." + }, + "input": { + "type": "string", + "description": "Text to embed: a single string, or an array of strings for batch embedding" + }, + "keep_alive": { + "type": "string", + "description": "Duration to keep model in memory: '5m', '1h', or 0 to unload immediately" + }, + "model": { + "type": "string", + "description": "Embedding model name (e.g. 'all-minilm', 'nomic-embed-text'). Must support embeddings — general chat models will return an error." + }, + "options": { + "type": "string", + "description": "Model parameters: seed, num_ctx, etc." + }, + "truncate": { + "type": "string", + "description": "Truncate input exceeding context window (default true). Set false to return an error instead of silently truncating." + } + }, + "required": [ + "input", + "model" + ] + } + }, + { + "name": "gcp_get_project", + "description": "Get details about the configured GCP project. Start here to verify project access.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_list_projects", + "description": "Search for GCP projects the caller has access to", + "inputSchema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "Filter query (e.g. name:my-project)" + } + } + } + }, + { + "name": "gcp_list_folders", + "description": "List folders under a parent resource", + "inputSchema": { + "type": "object", + "properties": { + "parent": { + "type": "string", + "description": "Parent resource name (e.g. organizations/123)" + } + }, + "required": [ + "parent" + ] + } + }, + { + "name": "gcp_get_folder", + "description": "Get details about a folder", + "inputSchema": { + "type": "object", + "properties": { + "folder_id": { + "type": "string", + "description": "Folder ID" + } + }, + "required": [ + "folder_id" + ] + } + }, + { + "name": "gcp_get_iam_policy", + "description": "Get IAM policy for the configured project", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_storage_list_buckets", + "description": "List all Cloud Storage buckets in the project", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Maximum number of buckets to return (default 1000)" + } + } + } + }, + { + "name": "gcp_storage_get_bucket", + "description": "Get metadata for a Cloud Storage bucket", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + } + }, + "required": [ + "bucket" + ] + } + }, + { + "name": "gcp_storage_list_objects", + "description": "List objects in a Cloud Storage bucket (default limit 1000)", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "delimiter": { + "type": "string", + "description": "Delimiter for hierarchy (e.g. /)" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 1000)" + }, + "prefix": { + "type": "string", + "description": "Object name prefix filter" + } + }, + "required": [ + "bucket" + ] + } + }, + { + "name": "gcp_storage_get_object", + "description": "Get an object from Cloud Storage (max 10MB; returns metadata and body as text for text types, base64 for binary)", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "object": { + "type": "string", + "description": "Object name/path" + } + }, + "required": [ + "bucket", + "object" + ] + } + }, + { + "name": "gcp_storage_put_object", + "description": "Upload an object to Cloud Storage", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Object content (text)" + }, + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "content_type": { + "type": "string", + "description": "MIME type (default: application/octet-stream)" + }, + "object": { + "type": "string", + "description": "Object name/path" + } + }, + "required": [ + "body", + "bucket", + "object" + ] + } + }, + { + "name": "gcp_storage_delete_object", + "description": "Delete an object from Cloud Storage", + "inputSchema": { + "type": "object", + "properties": { + "bucket": { + "type": "string", + "description": "Bucket name" + }, + "object": { + "type": "string", + "description": "Object name/path" + } + }, + "required": [ + "bucket", + "object" + ] + } + }, + { + "name": "gcp_storage_copy_object", + "description": "Copy an object within Cloud Storage", + "inputSchema": { + "type": "object", + "properties": { + "dest_bucket": { + "type": "string", + "description": "Destination bucket name" + }, + "dest_object": { + "type": "string", + "description": "Destination object name" + }, + "source_bucket": { + "type": "string", + "description": "Source bucket name" + }, + "source_object": { + "type": "string", + "description": "Source object name" + } + }, + "required": [ + "dest_bucket", + "dest_object", + "source_bucket", + "source_object" + ] + } + }, + { + "name": "gcp_compute_list_instances", + "description": "List Compute Engine VM instances (virtual machines) in a zone. View production server infrastructure. Default limit 500.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 500)" + }, + "max_results": { + "type": "string", + "description": "Page size for API requests" + }, + "zone": { + "type": "string", + "description": "Zone name (e.g. us-central1-a)" + } + }, + "required": [ + "zone" + ] + } + }, + { + "name": "gcp_compute_get_instance", + "description": "Get details for a specific Compute Engine instance", + "inputSchema": { + "type": "object", + "properties": { + "instance": { + "type": "string", + "description": "Instance name" + }, + "zone": { + "type": "string", + "description": "Zone name" + } + }, + "required": [ + "instance", + "zone" + ] + } + }, + { + "name": "gcp_compute_start_instance", + "description": "Start a Compute Engine instance", + "inputSchema": { + "type": "object", + "properties": { + "instance": { + "type": "string", + "description": "Instance name" + }, + "zone": { + "type": "string", + "description": "Zone name" + } + }, + "required": [ + "instance", + "zone" + ] + } + }, + { + "name": "gcp_compute_stop_instance", + "description": "Stop a Compute Engine instance", + "inputSchema": { + "type": "object", + "properties": { + "instance": { + "type": "string", + "description": "Instance name" + }, + "zone": { + "type": "string", + "description": "Zone name" + } + }, + "required": [ + "instance", + "zone" + ] + } + }, + { + "name": "gcp_compute_list_disks", + "description": "List persistent disks in a zone (default limit 500)", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 500)" + }, + "max_results": { + "type": "string", + "description": "Page size for API requests" + }, + "zone": { + "type": "string", + "description": "Zone name" + } + }, + "required": [ + "zone" + ] + } + }, + { + "name": "gcp_compute_list_networks", + "description": "List VPC networks in the project (default limit 500)", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 500)" + }, + "max_results": { + "type": "string", + "description": "Page size for API requests" + } + } + } + }, + { + "name": "gcp_compute_list_subnetworks", + "description": "List subnetworks in a region (default limit 500)", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 500)" + }, + "max_results": { + "type": "string", + "description": "Page size for API requests" + }, + "region": { + "type": "string", + "description": "Region name (e.g. us-central1)" + } + }, + "required": [ + "region" + ] + } + }, + { + "name": "gcp_compute_list_firewalls", + "description": "List firewall rules in the project (default limit 500)", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "limit": { + "type": "string", + "description": "Maximum total results to return (default 500)" + }, + "max_results": { + "type": "string", + "description": "Page size for API requests" + } + } + } + }, + { + "name": "gcp_compute_get_firewall", + "description": "Get details for a specific firewall rule", + "inputSchema": { + "type": "object", + "properties": { + "firewall": { + "type": "string", + "description": "Firewall rule name" + } + }, + "required": [ + "firewall" + ] + } + }, + { + "name": "gcp_functions_list", + "description": "List Cloud Functions (serverless) in a location. View deployed functions and their configurations.", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "Location (e.g. us-central1, or - for all locations)" + } + } + } + }, + { + "name": "gcp_functions_get", + "description": "Get details about a Cloud Function", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full resource name (projects/PROJECT/locations/LOCATION/functions/NAME)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_functions_get_iam_policy", + "description": "Get IAM policy for a Cloud Function", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full resource name of the function" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_iam_list_service_accounts", + "description": "List service accounts (automation identities) in the project. Review access credentials and security configuration.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_iam_get_service_account", + "description": "Get details about a service account", + "inputSchema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Service account email" + } + }, + "required": [ + "email" + ] + } + }, + { + "name": "gcp_iam_list_service_account_keys", + "description": "List keys for a service account", + "inputSchema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Service account email" + } + }, + "required": [ + "email" + ] + } + }, + { + "name": "gcp_iam_list_roles", + "description": "List predefined and custom IAM roles", + "inputSchema": { + "type": "object", + "properties": { + "show_deleted": { + "type": "string", + "description": "Include deleted roles (true/false)" + } + } + } + }, + { + "name": "gcp_iam_get_role", + "description": "Get details about an IAM role", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Role name (e.g. roles/editor or projects/PROJECT/roles/CUSTOM)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_monitoring_list_metric_descriptors", + "description": "List available metric descriptors", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Metric filter (e.g. metric.type = starts_with(\"compute.googleapis.com\"))" + } + } + } + }, + { + "name": "gcp_monitoring_list_time_series", + "description": "Get Cloud Monitoring time series data for a metric. Query production performance, CPU, memory, and custom metric graphs.", + "inputSchema": { + "type": "object", + "properties": { + "alignment_period": { + "type": "string", + "description": "Alignment period in seconds (e.g. 60s)" + }, + "end_time": { + "type": "string", + "description": "End time (RFC3339, default now)" + }, + "filter": { + "type": "string", + "description": "Time series filter (e.g. metric.type=\"compute.googleapis.com/instance/cpu/utilization\")" + }, + "per_series_aligner": { + "type": "string", + "description": "Aligner: ALIGN_MEAN, ALIGN_SUM, ALIGN_MAX, ALIGN_MIN, etc." + }, + "start_time": { + "type": "string", + "description": "Start time (RFC3339)" + } + }, + "required": [ + "filter", + "start_time" + ] + } + }, + { + "name": "gcp_monitoring_list_alert_policies", + "description": "List Cloud Monitoring alert policies for production threshold warnings and notifications", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + } + } + } + }, + { + "name": "gcp_monitoring_get_alert_policy", + "description": "Get details about an alert policy", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full resource name of the alert policy" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_monitoring_list_monitored_resources", + "description": "List monitored resource descriptors", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + } + } + } + }, + { + "name": "gcp_run_list_services", + "description": "List Cloud Run serverless container services in a location. View production deployments and configurations.", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "Location (e.g. us-central1)" + } + }, + "required": [ + "location" + ] + } + }, + { + "name": "gcp_run_get_service", + "description": "Get details about a Cloud Run service", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full resource name (projects/PROJECT/locations/LOCATION/services/NAME)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_run_list_revisions", + "description": "List revisions of a Cloud Run service", + "inputSchema": { + "type": "object", + "properties": { + "service_name": { + "type": "string", + "description": "Full resource name of the parent service" + } + }, + "required": [ + "service_name" + ] + } + }, + { + "name": "gcp_run_get_revision", + "description": "Get details about a Cloud Run revision", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full resource name of the revision" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gcp_pubsub_list_topics", + "description": "List Pub/Sub topics in the project", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_pubsub_get_topic", + "description": "Get configuration for a Pub/Sub topic", + "inputSchema": { + "type": "object", + "properties": { + "topic": { + "type": "string", + "description": "Topic ID" + } + }, + "required": [ + "topic" + ] + } + }, + { + "name": "gcp_pubsub_publish", + "description": "Publish a message to a Pub/Sub topic", + "inputSchema": { + "type": "object", + "properties": { + "attributes": { + "type": "string", + "description": "JSON object of message attributes" + }, + "message": { + "type": "string", + "description": "Message data (text)" + }, + "topic": { + "type": "string", + "description": "Topic ID" + } + }, + "required": [ + "message", + "topic" + ] + } + }, + { + "name": "gcp_pubsub_list_subscriptions", + "description": "List Pub/Sub subscriptions in the project", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_pubsub_get_subscription", + "description": "Get configuration for a Pub/Sub subscription", + "inputSchema": { + "type": "object", + "properties": { + "subscription": { + "type": "string", + "description": "Subscription ID" + } + }, + "required": [ + "subscription" + ] + } + }, + { + "name": "gcp_pubsub_pull", + "description": "Pull messages from a Pub/Sub subscription", + "inputSchema": { + "type": "object", + "properties": { + "max_messages": { + "type": "string", + "description": "Maximum messages to pull (default 10)" + }, + "subscription": { + "type": "string", + "description": "Subscription ID" + }, + "timeout": { + "type": "string", + "description": "Timeout in seconds to wait for messages (default 10)" + } + }, + "required": [ + "subscription" + ] + } + }, + { + "name": "gcp_firestore_list_collections", + "description": "List top-level collections in Firestore", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_firestore_list_documents", + "description": "List documents in a Firestore collection", + "inputSchema": { + "type": "object", + "properties": { + "collection": { + "type": "string", + "description": "Collection path" + }, + "limit": { + "type": "string", + "description": "Maximum number of documents to return" + } + }, + "required": [ + "collection" + ] + } + }, + { + "name": "gcp_firestore_get_document", + "description": "Get a document from Firestore", + "inputSchema": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Document path (e.g. collection/docId)" + } + }, + "required": [ + "path" + ] + } + }, + { + "name": "gcp_firestore_set_document", + "description": "Create or update a document in Firestore", + "inputSchema": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "JSON object with document fields" + }, + "path": { + "type": "string", + "description": "Document path (e.g. collection/docId)" + } + }, + "required": [ + "data", + "path" + ] + } + }, + { + "name": "gcp_firestore_delete_document", + "description": "Delete a document from Firestore", + "inputSchema": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Document path (e.g. collection/docId)" + } + }, + "required": [ + "path" + ] + } + }, + { + "name": "gcp_firestore_query", + "description": "Query documents in a Firestore collection", + "inputSchema": { + "type": "object", + "properties": { + "collection": { + "type": "string", + "description": "Collection path" + }, + "limit": { + "type": "string", + "description": "Maximum number of documents" + }, + "order_by": { + "type": "string", + "description": "Field to order by" + }, + "order_dir": { + "type": "string", + "description": "Order direction: asc or desc" + }, + "where_field": { + "type": "string", + "description": "Field to filter on" + }, + "where_op": { + "type": "string", + "description": "Operator: ==, !=, \u003c, \u003c=, \u003e, \u003e=, array-contains, in" + }, + "where_value": { + "type": "string", + "description": "Value to compare (JSON-encoded: use 123 for numbers, \"\\\"text\\\"\" for strings, true/false for booleans)" + } + }, + "required": [ + "collection" + ] + } + }, + { + "name": "gcp_logging_list_entries", + "description": "List Cloud Logging entries for the project. Search production logs for errors, debugging, and observability.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Logging filter (e.g. severity\u003e=ERROR)" + }, + "order_by": { + "type": "string", + "description": "Order: timestamp asc or timestamp desc (default desc)" + }, + "page_size": { + "type": "string", + "description": "Maximum entries to return (default 50)" + } + } + } + }, + { + "name": "gcp_logging_list_log_names", + "description": "List log names in the project", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_logging_list_sinks", + "description": "List logging sinks in the project", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gcp_logging_get_sink", + "description": "Get details about a logging sink", + "inputSchema": { + "type": "object", + "properties": { + "sink_name": { + "type": "string", + "description": "Sink name" + } + }, + "required": [ + "sink_name" + ] + } + }, + { + "name": "suno_generate_music", + "description": "Generate a music track with Suno AI. Each request produces 2 songs. Start here for music creation workflows. Stream URL available in ~30s, download URL in ~2-3 min. Use suno_get_generation to poll status", + "inputSchema": { + "type": "object", + "properties": { + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "custom_mode": { + "type": "string", + "description": "Enable custom mode for fine control over style/title/lyrics (true/false, default: true)" + }, + "instrumental": { + "type": "string", + "description": "Generate instrumental only, no vocals (true/false, default: false)" + }, + "model": { + "type": "string", + "description": "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)" + }, + "negative_tags": { + "type": "string", + "description": "Styles to avoid (e.g. 'Heavy Metal, Upbeat Drums')" + }, + "persona_id": { + "type": "string", + "description": "Persona ID for personalized style" + }, + "prompt": { + "type": "string", + "description": "Text description or lyrics for the song (500 chars in non-custom mode, up to 5000 in custom mode)" + }, + "style": { + "type": "string", + "description": "Music genre/style (e.g. pop, rock, jazz, folk, synthwave). Required in custom mode" + }, + "style_weight": { + "type": "string", + "description": "Style influence weight 0-1 (default: 0.65)" + }, + "title": { + "type": "string", + "description": "Song title (max 80-100 chars depending on model). Required in custom mode" + }, + "vocal_gender": { + "type": "string", + "description": "Vocal gender: m or f" + } + }, + "required": [ + "prompt" + ] + } + }, + { + "name": "suno_get_generation", + "description": "Check the status of a music generation task. Returns track URLs when complete. Status values: PENDING, TEXT_SUCCESS, FIRST_SUCCESS, SUCCESS, FAILED", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID returned from suno_generate_music or other generation tools" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "suno_extend_music", + "description": "Extend an existing audio track with additional content. Creates a continuation from a specific timestamp", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track to extend" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "continue_at": { + "type": "string", + "description": "Time in seconds to start extension from" + }, + "model": { + "type": "string", + "description": "Model version: V5, V4_5PLUS, V4_5ALL, V4_5, V4 (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Description or lyrics for the extension" + }, + "style": { + "type": "string", + "description": "Music style for the extension" + }, + "title": { + "type": "string", + "description": "Title for the extended track" + }, + "use_default_params": { + "type": "string", + "description": "Use original track parameters instead of custom (true/false, default: false)" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_get_credits", + "description": "Get the number of remaining generation credits for the authenticated account", + "inputSchema": { + "type": "object" + } + }, + { + "name": "suno_generate_lyrics", + "description": "Generate song lyrics from a text prompt. Max 200 characters. Use suno_get_lyrics to poll for results", + "inputSchema": { + "type": "object", + "properties": { + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "prompt": { + "type": "string", + "description": "Description of desired lyrics (max 200 chars)" + } + }, + "required": [ + "prompt" + ] + } + }, + { + "name": "suno_get_lyrics", + "description": "Get the status and result of a lyrics generation task", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID from suno_generate_lyrics" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "suno_get_aligned_lyrics", + "description": "Get timestamped/word-level aligned lyrics for an audio track. Useful for karaoke or synced display", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_separate_stems", + "description": "Separate an audio track into vocal and instrumental stems. Use suno_get_stem_separation to poll status", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track to separate" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_get_stem_separation", + "description": "Get the status and URLs of a stem separation task. Returns vocal and instrumental track URLs when complete", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID from suno_separate_stems" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "suno_convert_wav", + "description": "Convert a generated audio track to WAV format. Use suno_get_wav_conversion to poll status", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track to convert" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_get_wav_conversion", + "description": "Get the status and download URL of a WAV conversion task", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID from suno_convert_wav" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "suno_cover_audio", + "description": "Create a cover version of uploaded audio with a new style and arrangement", + "inputSchema": { + "type": "object", + "properties": { + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "custom_mode": { + "type": "string", + "description": "Enable custom mode (true/false, default: true)" + }, + "model": { + "type": "string", + "description": "Model version (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Description or lyrics for the cover" + }, + "style": { + "type": "string", + "description": "Target music style for the cover" + }, + "title": { + "type": "string", + "description": "Title for the cover version" + }, + "upload_url": { + "type": "string", + "description": "URL of the source audio file" + } + }, + "required": [ + "upload_url" + ] + } + }, + { + "name": "suno_upload_extend", + "description": "Upload audio and extend it with AI-generated continuation", + "inputSchema": { + "type": "object", + "properties": { + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "model": { + "type": "string", + "description": "Model version (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Description or lyrics for the extension" + }, + "style": { + "type": "string", + "description": "Music style for the extension" + }, + "title": { + "type": "string", + "description": "Title for the extended track" + }, + "upload_url": { + "type": "string", + "description": "URL of the audio file to extend" + } + }, + "required": [ + "upload_url" + ] + } + }, + { + "name": "suno_add_vocals", + "description": "Generate vocal tracks for instrumental music", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the instrumental audio track" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "model": { + "type": "string", + "description": "Model version (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Lyrics or vocal description" + }, + "style": { + "type": "string", + "description": "Vocal style" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_add_instrumental", + "description": "Generate instrumental accompaniment for a vocal track", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the vocal audio track" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "model": { + "type": "string", + "description": "Model version (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Instrumental description" + }, + "style": { + "type": "string", + "description": "Instrumental style" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_generate_mashup", + "description": "Generate a mashup combining elements from multiple tracks", + "inputSchema": { + "type": "object", + "properties": { + "audio_ids": { + "type": "string", + "description": "Comma-separated list of audio IDs to mashup" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "model": { + "type": "string", + "description": "Model version (default: V4_5ALL)" + }, + "prompt": { + "type": "string", + "description": "Description of the desired mashup" + }, + "style": { + "type": "string", + "description": "Target style for the mashup" + } + }, + "required": [ + "audio_ids" + ] + } + }, + { + "name": "suno_generate_persona", + "description": "Create a personalized music persona based on generated tracks. Returns a persona_id for use in suno_generate_music", + "inputSchema": { + "type": "object", + "properties": { + "audio_ids": { + "type": "string", + "description": "Comma-separated list of audio IDs to base the persona on" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + } + }, + "required": [ + "audio_ids" + ] + } + }, + { + "name": "suno_generate_video", + "description": "Generate a music video from an audio track. Use suno_get_video to poll status", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track" + }, + "author": { + "type": "string", + "description": "Author/artist name for the video" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + }, + "domain_name": { + "type": "string", + "description": "Brand domain name for the video" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "suno_get_video", + "description": "Get the status and URL of a video generation task", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "Task ID from suno_generate_video" + } + }, + "required": [ + "task_id" + ] + } + }, + { + "name": "suno_generate_midi", + "description": "Generate a MIDI file from an audio track", + "inputSchema": { + "type": "object", + "properties": { + "audio_id": { + "type": "string", + "description": "ID of the audio track" + }, + "callback_url": { + "type": "string", + "description": "Webhook URL for completion notification" + } + }, + "required": [ + "audio_id" + ] + } + }, + { + "name": "salesforce_describe_global", + "description": "List all SObjects available in the org with metadata (name, label, keyPrefix, queryable, createable, etc.). Start here for schema discovery.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "salesforce_describe_sobject", + "description": "Get detailed metadata for an SObject type including all fields, record types, child relationships, and URLs", + "inputSchema": { + "type": "object", + "properties": { + "sobject": { + "type": "string", + "description": "SObject type name (e.g. Account, Contact, Lead, Opportunity, Case, custom__c)" + } + }, + "required": [ + "sobject" + ] + } + }, + { + "name": "salesforce_get_record", + "description": "Get a single Salesforce record by ID. Optionally specify fields to return.", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Comma-separated field names to return (e.g. Id,Name,Email). Returns all fields if omitted." + }, + "id": { + "type": "string", + "description": "Record ID (15 or 18 character Salesforce ID)" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact, Lead, Opportunity)" + } + }, + "required": [ + "id", + "sobject" + ] + } + }, + { + "name": "salesforce_create_record", + "description": "Create a new Salesforce record. Provide field values as a JSON object.", + "inputSchema": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "JSON string of field name/value pairs (e.g. {\"Name\":\"Acme\",\"Industry\":\"Technology\"})" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact, Lead, Opportunity)" + } + }, + "required": [ + "data", + "sobject" + ] + } + }, + { + "name": "salesforce_update_record", + "description": "Update an existing Salesforce record. Provide only the fields to change.", + "inputSchema": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "JSON string of field name/value pairs to update" + }, + "id": { + "type": "string", + "description": "Record ID (15 or 18 character Salesforce ID)" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact, Lead, Opportunity)" + } + }, + "required": [ + "data", + "id", + "sobject" + ] + } + }, + { + "name": "salesforce_delete_record", + "description": "Delete a Salesforce record by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Record ID (15 or 18 character Salesforce ID)" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact, Lead, Opportunity)" + } + }, + "required": [ + "id", + "sobject" + ] + } + }, + { + "name": "salesforce_get_record_by_external_id", + "description": "Get a Salesforce record using an external ID field value", + "inputSchema": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "External ID field name" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact)" + }, + "value": { + "type": "string", + "description": "External ID value" + } + }, + "required": [ + "field", + "sobject", + "value" + ] + } + }, + { + "name": "salesforce_upsert_by_external_id", + "description": "Create or update a record using an external ID. Creates if not found, updates if exists.", + "inputSchema": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "JSON string of field name/value pairs" + }, + "field": { + "type": "string", + "description": "External ID field name" + }, + "sobject": { + "type": "string", + "description": "SObject type (e.g. Account, Contact)" + }, + "value": { + "type": "string", + "description": "External ID value" + } + }, + "required": [ + "data", + "field", + "sobject", + "value" + ] + } + }, + { + "name": "salesforce_query", + "description": "Execute a SOQL query to retrieve Salesforce records. Supports SELECT, WHERE, ORDER BY, LIMIT, GROUP BY, and relationship queries.", + "inputSchema": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "SOQL query string (e.g. SELECT Id, Name FROM Account WHERE Industry = 'Technology' LIMIT 10)" + } + }, + "required": [ + "q" + ] + } + }, + { + "name": "salesforce_query_more", + "description": "Retrieve the next batch of SOQL query results using a nextRecordsUrl from a previous query response", + "inputSchema": { + "type": "object", + "properties": { + "next_url": { + "type": "string", + "description": "The nextRecordsUrl value from a previous query response (e.g. /services/data/v62.0/query/01gxx0000...)" + } + }, + "required": [ + "next_url" + ] + } + }, + { + "name": "salesforce_search", + "description": "Execute a SOSL full-text search across multiple Salesforce objects. Use FIND clause with search terms.", + "inputSchema": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "SOSL search string (e.g. FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name), Contact(Id, Name, Email))" + } + }, + "required": [ + "q" + ] + } + }, + { + "name": "salesforce_list_api_versions", + "description": "List all available Salesforce REST API versions", + "inputSchema": { + "type": "object" + } + }, + { + "name": "salesforce_get_limits", + "description": "Get org API usage limits and current consumption (DailyApiRequests, DailyBulkApiRequests, etc.)", + "inputSchema": { + "type": "object" + } + }, + { + "name": "salesforce_list_recently_viewed", + "description": "List recently viewed records across all object types or for a specific object type", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Max number of records to return (default 200)" + } + } + } + }, + { + "name": "salesforce_composite_batch", + "description": "Execute up to 25 independent subrequests in a single API call. Each subrequest is a REST API request.", + "inputSchema": { + "type": "object", + "properties": { + "requests": { + "type": "string", + "description": "JSON array of batch requests, each with method, url, and optional richInput (e.g. [{\"method\":\"GET\",\"url\":\"/services/data/v62.0/sobjects/Account/001xx\"}])" + } + }, + "required": [ + "requests" + ] + } + }, + { + "name": "salesforce_sobject_collections", + "description": "Create, update, or delete up to 200 records of the same or different types in a single request", + "inputSchema": { + "type": "object", + "properties": { + "all_or_none": { + "type": "string", + "description": "If true, roll back all if any fail (default false)" + }, + "ids": { + "type": "string", + "description": "Comma-separated record IDs (required for DELETE only)" + }, + "method": { + "type": "string", + "description": "HTTP method: POST (create), PATCH (update), or DELETE (delete)" + }, + "records": { + "type": "string", + "description": "JSON array of records with attributes.type for create/update (e.g. [{\"attributes\":{\"type\":\"Account\"},\"Name\":\"Acme\"}])" + } + }, + "required": [ + "method" + ] + } + }, + { + "name": "cloudflare_list_zones", + "description": "List Cloudflare zones (domains). Start here for DNS management, CDN configuration, and website performance. Zones represent domains registered with Cloudflare.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Zone name filter (exact domain match, e.g. example.com)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20, max 50)" + }, + "status": { + "type": "string", + "description": "Zone status filter (active, pending, initializing, moved, deleted, deactivated, read_only)" + } + } + } + }, + { + "name": "cloudflare_get_zone", + "description": "Get details for a specific Cloudflare zone including status, nameservers, and settings. Use after list_zones.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_create_zone", + "description": "Add a new domain (zone) to Cloudflare. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "jump_start": { + "type": "string", + "description": "Auto-fetch DNS records (true/false, default false)" + }, + "name": { + "type": "string", + "description": "Domain name (e.g. example.com)" + }, + "type": { + "type": "string", + "description": "Zone type: full (default) or partial" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "cloudflare_edit_zone", + "description": "Update zone settings like paused status or vanity nameservers. Use after get_zone.", + "inputSchema": { + "type": "object", + "properties": { + "paused": { + "type": "string", + "description": "Pause Cloudflare for this zone (true/false)" + }, + "plan": { + "type": "string", + "description": "Plan identifier to change zone plan" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_delete_zone", + "description": "Remove a zone (domain) from Cloudflare. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_purge_cache", + "description": "Purge cached content for a Cloudflare zone. Purge everything or specific URLs/tags/hosts. Use for cache invalidation after deployments.", + "inputSchema": { + "type": "object", + "properties": { + "files": { + "type": "string", + "description": "JSON array of URLs to purge (alternative to purge_everything)" + }, + "hosts": { + "type": "string", + "description": "Comma-separated hostnames to purge" + }, + "purge_everything": { + "type": "string", + "description": "Purge all cached content (true/false)" + }, + "tags": { + "type": "string", + "description": "Comma-separated cache tags to purge" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_dns_records", + "description": "List DNS records for a Cloudflare zone. View A, AAAA, CNAME, MX, TXT, NS, and other record types. Start here for DNS management and troubleshooting.", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "DNS record content filter (e.g. IP address)" + }, + "name": { + "type": "string", + "description": "DNS record name filter (e.g. subdomain.example.com)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20, max 100)" + }, + "type": { + "type": "string", + "description": "DNS record type filter (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_get_dns_record", + "description": "Get details for a specific DNS record. Use after list_dns_records.", + "inputSchema": { + "type": "object", + "properties": { + "record_id": { + "type": "string", + "description": "DNS record identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "record_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_create_dns_record", + "description": "Create a new DNS record in a Cloudflare zone. Add A, AAAA, CNAME, MX, TXT records and more.", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "DNS record content (e.g. IP address, target domain)" + }, + "name": { + "type": "string", + "description": "DNS record name (e.g. subdomain or @ for root)" + }, + "priority": { + "type": "string", + "description": "MX/SRV record priority" + }, + "proxied": { + "type": "string", + "description": "Whether traffic is proxied through Cloudflare (true/false)" + }, + "ttl": { + "type": "string", + "description": "TTL in seconds (1 = automatic, default 1)" + }, + "type": { + "type": "string", + "description": "DNS record type (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "content", + "name", + "type", + "zone_id" + ] + } + }, + { + "name": "cloudflare_update_dns_record", + "description": "Update an existing DNS record. Use after list_dns_records to get record_id.", + "inputSchema": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "DNS record content" + }, + "name": { + "type": "string", + "description": "DNS record name" + }, + "priority": { + "type": "string", + "description": "MX/SRV record priority" + }, + "proxied": { + "type": "string", + "description": "Whether traffic is proxied through Cloudflare (true/false)" + }, + "record_id": { + "type": "string", + "description": "DNS record identifier" + }, + "ttl": { + "type": "string", + "description": "TTL in seconds (1 = automatic)" + }, + "type": { + "type": "string", + "description": "DNS record type" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "content", + "name", + "record_id", + "type", + "zone_id" + ] + } + }, + { + "name": "cloudflare_delete_dns_record", + "description": "Delete a DNS record from a Cloudflare zone.", + "inputSchema": { + "type": "object", + "properties": { + "record_id": { + "type": "string", + "description": "DNS record identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "record_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_workers", + "description": "List Cloudflare Workers scripts. View serverless functions, edge computing workers, and deployed scripts. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_worker", + "description": "Get metadata for a specific Cloudflare Worker script including bindings, routes, and compatibility settings. Use after list_workers.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "script_name": { + "type": "string", + "description": "Worker script name" + } + }, + "required": [ + "script_name" + ] + } + }, + { + "name": "cloudflare_delete_worker", + "description": "Delete a Cloudflare Worker script.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "script_name": { + "type": "string", + "description": "Worker script name" + } + }, + "required": [ + "script_name" + ] + } + }, + { + "name": "cloudflare_list_worker_routes", + "description": "List Worker routes for a zone. Shows URL patterns mapped to Worker scripts.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_pages_projects", + "description": "List Cloudflare Pages projects. View static sites, Jamstack deployments, and frontend projects hosted on Pages. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_pages_project", + "description": "Get details for a Cloudflare Pages project including build config, domains, and deployment settings. Use after list_pages_projects.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "project_name" + ] + } + }, + { + "name": "cloudflare_list_pages_deployments", + "description": "List deployments for a Cloudflare Pages project. View deploy history, build status, and rollback targets.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "project_name" + ] + } + }, + { + "name": "cloudflare_get_pages_deployment", + "description": "Get details for a specific Pages deployment including build logs and environment. Use after list_pages_deployments.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "deployment_id": { + "type": "string", + "description": "Deployment identifier" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "deployment_id", + "project_name" + ] + } + }, + { + "name": "cloudflare_delete_pages_deployment", + "description": "Delete a specific Pages deployment.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "deployment_id": { + "type": "string", + "description": "Deployment identifier" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "deployment_id", + "project_name" + ] + } + }, + { + "name": "cloudflare_rollback_pages_deployment", + "description": "Rollback a Pages project to a previous deployment. Use after list_pages_deployments to find deployment_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "deployment_id": { + "type": "string", + "description": "Deployment identifier to rollback to" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "deployment_id", + "project_name" + ] + } + }, + { + "name": "cloudflare_list_r2_buckets", + "description": "List R2 object storage buckets. Cloudflare R2 is S3-compatible storage with zero egress fees. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_create_r2_bucket", + "description": "Create a new R2 object storage bucket.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "name": { + "type": "string", + "description": "Bucket name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "cloudflare_delete_r2_bucket", + "description": "Delete an R2 object storage bucket. Bucket must be empty.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "name": { + "type": "string", + "description": "Bucket name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "cloudflare_list_kv_namespaces", + "description": "List Workers KV namespaces. KV is Cloudflare's global key-value store for edge data. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_create_kv_namespace", + "description": "Create a new Workers KV namespace.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "title": { + "type": "string", + "description": "Namespace title" + } + }, + "required": [ + "title" + ] + } + }, + { + "name": "cloudflare_delete_kv_namespace", + "description": "Delete a Workers KV namespace and all its keys.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + } + }, + "required": [ + "namespace_id" + ] + } + }, + { + "name": "cloudflare_list_kv_keys", + "description": "List keys in a Workers KV namespace. Returns key names with optional metadata.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "limit": { + "type": "string", + "description": "Maximum keys to return (default 1000)" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + }, + "prefix": { + "type": "string", + "description": "Key prefix filter" + } + }, + "required": [ + "namespace_id" + ] + } + }, + { + "name": "cloudflare_get_kv_value", + "description": "Read a value from Workers KV by key. Returns the stored value as text.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "key_name": { + "type": "string", + "description": "Key name to read" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + } + }, + "required": [ + "key_name", + "namespace_id" + ] + } + }, + { + "name": "cloudflare_put_kv_value", + "description": "Write a key-value pair to Workers KV.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "key_name": { + "type": "string", + "description": "Key name" + }, + "metadata": { + "type": "string", + "description": "JSON metadata object to attach to the key" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + }, + "value": { + "type": "string", + "description": "Value to store" + } + }, + "required": [ + "key_name", + "namespace_id", + "value" + ] + } + }, + { + "name": "cloudflare_delete_kv_value", + "description": "Delete a key-value pair from Workers KV.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "key_name": { + "type": "string", + "description": "Key name to delete" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + } + }, + "required": [ + "key_name", + "namespace_id" + ] + } + }, + { + "name": "cloudflare_list_d1_databases", + "description": "List D1 SQL databases. D1 is Cloudflare's serverless SQLite database for Workers. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_d1_database", + "description": "Get details for a specific D1 database including size and table count. Use after list_d1_databases.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "database_id": { + "type": "string", + "description": "D1 database identifier" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "cloudflare_create_d1_database", + "description": "Create a new D1 SQL database.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "name": { + "type": "string", + "description": "Database name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "cloudflare_delete_d1_database", + "description": "Delete a D1 SQL database and all its data. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "database_id": { + "type": "string", + "description": "D1 database identifier" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "cloudflare_query_d1_database", + "description": "Execute a SQL query against a D1 database. Supports SELECT, INSERT, UPDATE, DELETE and DDL. Use parameterized queries with params array for safety.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "database_id": { + "type": "string", + "description": "D1 database identifier" + }, + "params": { + "type": "string", + "description": "JSON array of query parameters for prepared statements" + }, + "sql": { + "type": "string", + "description": "SQL query to execute" + } + }, + "required": [ + "database_id", + "sql" + ] + } + }, + { + "name": "cloudflare_list_waf_rulesets", + "description": "List WAF (Web Application Firewall) rulesets for a zone. View security rules protecting against attacks, bots, and threats.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_get_waf_ruleset", + "description": "Get details for a specific WAF ruleset including individual rules and their actions. Use after list_waf_rulesets.", + "inputSchema": { + "type": "object", + "properties": { + "ruleset_id": { + "type": "string", + "description": "Ruleset identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "ruleset_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_load_balancers", + "description": "List load balancers for a zone. View traffic distribution, health checks, and failover configuration.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_get_load_balancer", + "description": "Get details for a specific load balancer including pools, rules, and session affinity. Use after list_load_balancers.", + "inputSchema": { + "type": "object", + "properties": { + "lb_id": { + "type": "string", + "description": "Load balancer identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "lb_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_lb_pools", + "description": "List load balancer pools (origin server groups). View pool health, origins, and traffic steering. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_lb_pool", + "description": "Get details for a specific load balancer pool including origins and health check results. Use after list_lb_pools.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "pool_id": { + "type": "string", + "description": "Pool identifier" + } + }, + "required": [ + "pool_id" + ] + } + }, + { + "name": "cloudflare_list_lb_monitors", + "description": "List load balancer health monitors. View health check configurations for origin pools. Requires account_id.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_zone_analytics", + "description": "Get zone analytics dashboard data including requests, bandwidth, threats, and page views. Provides traffic overview and performance metrics for a Cloudflare zone.", + "inputSchema": { + "type": "object", + "properties": { + "since": { + "type": "string", + "description": "Start time (ISO 8601 or relative like -1440 for minutes ago, default -1440)" + }, + "until": { + "type": "string", + "description": "End time (ISO 8601 or relative, default 0 for now)" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_accounts", + "description": "List Cloudflare accounts the API token has access to.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_get_account", + "description": "Get details for a specific Cloudflare account. Use after list_accounts.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_list_account_members", + "description": "List members of a Cloudflare account with their roles and permissions.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_list_ai_gateways", + "description": "List Cloudflare AI Gateways for an account. AI Gateway proxies LLM traffic to OpenAI, Anthropic, Workers AI, and other providers with caching, rate limiting, logging, and cost tracking. Start here to discover gateway IDs before pulling logs, token usage, or spend.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_get_ai_gateway", + "description": "Get a Cloudflare AI Gateway's configuration: cache TTL, rate limits, log collection, spend limits, authentication, DLP, and guardrails. Use after list_ai_gateways.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "gateway_id": { + "type": "string", + "description": "AI Gateway identifier" + } + }, + "required": [ + "gateway_id" + ] + } + }, + { + "name": "cloudflare_list_ai_gateway_logs", + "description": "List AI Gateway request logs with per-request token usage (tokens_in, tokens_out), cost, latency, model, provider, cache hits, and success/error status. Use this to audit LLM spend, debug failing inferences, or attribute token consumption to a model or provider. Filter by date, provider, model, success, cached, or feedback.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "cached": { + "type": "string", + "description": "Filter by cache hit (true/false)" + }, + "end_date": { + "type": "string", + "description": "End of time range (ISO 8601)" + }, + "feedback": { + "type": "string", + "description": "Filter by feedback rating (0 or 1)" + }, + "gateway_id": { + "type": "string", + "description": "AI Gateway identifier" + }, + "model": { + "type": "string", + "description": "Filter by model id (e.g. gpt-4o, claude-3-5-sonnet)" + }, + "order_by": { + "type": "string", + "description": "Sort field: created_at, provider, model, model_type, success, or cached" + }, + "order_by_direction": { + "type": "string", + "description": "Sort direction (asc or desc)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20, max 50)" + }, + "provider": { + "type": "string", + "description": "Filter by LLM provider (e.g. openai, anthropic, workers-ai)" + }, + "search": { + "type": "string", + "description": "Free-text search across log content" + }, + "start_date": { + "type": "string", + "description": "Start of time range (ISO 8601, e.g. 2024-01-01T00:00:00Z)" + }, + "success": { + "type": "string", + "description": "Filter by success (true/false)" + } + }, + "required": [ + "gateway_id" + ] + } + }, + { + "name": "cloudflare_get_ai_gateway_log", + "description": "Get a single AI Gateway log entry with full metadata: tokens_in, tokens_out, cost, model, provider, duration, cached, status. Use after list_ai_gateway_logs to drill into a specific request.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "gateway_id": { + "type": "string", + "description": "AI Gateway identifier" + }, + "log_id": { + "type": "string", + "description": "Log entry identifier" + } + }, + "required": [ + "gateway_id", + "log_id" + ] + } + }, + { + "name": "cloudflare_get_ai_gateway_log_request", + "description": "Get the raw request body (prompt, messages, parameters) sent to the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the exact prompt that produced a response.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "gateway_id": { + "type": "string", + "description": "AI Gateway identifier" + }, + "log_id": { + "type": "string", + "description": "Log entry identifier" + } + }, + "required": [ + "gateway_id", + "log_id" + ] + } + }, + { + "name": "cloudflare_get_ai_gateway_log_response", + "description": "Get the raw response body (completion, choices, usage) returned by the LLM for an AI Gateway log entry. Use after get_ai_gateway_log to inspect the model's reply or error.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "gateway_id": { + "type": "string", + "description": "AI Gateway identifier" + }, + "log_id": { + "type": "string", + "description": "Log entry identifier" + } + }, + "required": [ + "gateway_id", + "log_id" + ] + } + }, + { + "name": "cloudflare_list_ai_models", + "description": "List Workers AI models available to run on Cloudflare's edge. Workers AI hosts open-source LLMs, embeddings, image, and speech models. Start here to discover model ids (e.g. @cf/meta/llama-3.1-8b-instruct) before calling run_ai_model.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + }, + "search": { + "type": "string", + "description": "Free-text search filter (e.g. llama, embedding)" + }, + "source": { + "type": "string", + "description": "Filter by source (1 for first-party, 2 for Hugging Face)" + }, + "task": { + "type": "string", + "description": "Filter by task type (e.g. Text Generation, Text Embeddings, Speech Recognition)" + } + } + } + }, + { + "name": "cloudflare_run_ai_model", + "description": "Run inference on a Workers AI model — text generation, embeddings, classification, translation, speech-to-text, image generation. Pass either `prompt` (string) for simple text-in, or `body` (object) for the model's full request schema (messages, input, image, audio, etc.). Use after list_ai_models.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "body": { + "type": "string", + "description": "Full inference request body as a JSON object (model-specific schema)" + }, + "model_name": { + "type": "string", + "description": "Full model identifier (e.g. @cf/meta/llama-3.1-8b-instruct)" + }, + "prompt": { + "type": "string", + "description": "Convenience: text prompt (wrapped as {\"prompt\":...}). Ignored if `body` is set." + } + }, + "required": [ + "model_name" + ] + } + }, + { + "name": "cloudflare_list_vectorize_indexes", + "description": "List Vectorize v2 indexes. Vectorize is Cloudflare's globally distributed vector database for semantic search, RAG, and embeddings. Start here to discover index names before querying.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_vectorize_index", + "description": "Get a Vectorize index's configuration: dimensions, distance metric, vector count. Use after list_vectorize_indexes.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "index_name": { + "type": "string", + "description": "Vectorize index name" + } + }, + "required": [ + "index_name" + ] + } + }, + { + "name": "cloudflare_create_vectorize_index", + "description": "Create a new Vectorize v2 index for storing embeddings. Pick a metric (cosine, euclidean, dot-product) and dimension count (e.g. 768, 1536) that match your embedding model.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "description": { + "type": "string", + "description": "Optional human description" + }, + "dimensions": { + "type": "string", + "description": "Vector dimension count (e.g. 768, 1024, 1536)" + }, + "metric": { + "type": "string", + "description": "Distance metric: cosine, euclidean, or dot-product" + }, + "name": { + "type": "string", + "description": "Index name" + } + }, + "required": [ + "dimensions", + "metric", + "name" + ] + } + }, + { + "name": "cloudflare_delete_vectorize_index", + "description": "Delete a Vectorize index and all its vectors. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "index_name": { + "type": "string", + "description": "Vectorize index name" + } + }, + "required": [ + "index_name" + ] + } + }, + { + "name": "cloudflare_query_vectorize_index", + "description": "Query a Vectorize index by vector — returns the topK nearest neighbors. Use for semantic search, RAG retrieval, recommendation lookup.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "filter": { + "type": "string", + "description": "Optional metadata filter (JSON object)" + }, + "index_name": { + "type": "string", + "description": "Vectorize index name" + }, + "namespace": { + "type": "string", + "description": "Optional namespace to scope the query" + }, + "returnMetadata": { + "type": "string", + "description": "Whether to return metadata: 'none', 'indexed', or 'all'" + }, + "returnValues": { + "type": "string", + "description": "Whether to return the matched vectors (true/false)" + }, + "topK": { + "type": "string", + "description": "Number of nearest neighbors to return (default 5)" + }, + "vector": { + "type": "string", + "description": "Query vector as JSON array of floats" + } + }, + "required": [ + "index_name", + "vector" + ] + } + }, + { + "name": "cloudflare_list_queues", + "description": "List Cloudflare Queues. Queues are durable message queues for Workers — producer/consumer messaging, batch processing, async work. Start here to discover queue IDs.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_get_queue", + "description": "Get a Queue's details: producers, consumers, message counts, settings. Use after list_queues.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "queue_id": { + "type": "string", + "description": "Queue identifier" + } + }, + "required": [ + "queue_id" + ] + } + }, + { + "name": "cloudflare_create_queue", + "description": "Create a new Cloudflare Queue.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "queue_name": { + "type": "string", + "description": "Queue name" + }, + "settings": { + "type": "string", + "description": "Optional settings map (delivery_delay, message_retention_period, etc.)" + } + }, + "required": [ + "queue_name" + ] + } + }, + { + "name": "cloudflare_delete_queue", + "description": "Delete a Cloudflare Queue and all pending messages. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "queue_id": { + "type": "string", + "description": "Queue identifier" + } + }, + "required": [ + "queue_id" + ] + } + }, + { + "name": "cloudflare_send_queue_messages", + "description": "Publish messages to a Cloudflare Queue. Messages is a JSON array of objects with at least a `body` field.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "messages": { + "type": "string", + "description": "JSON array of message objects (each with `body` and optional `content_type`, `delay_seconds`)" + }, + "queue_id": { + "type": "string", + "description": "Queue identifier" + } + }, + "required": [ + "messages", + "queue_id" + ] + } + }, + { + "name": "cloudflare_list_hyperdrive_configs", + "description": "List Hyperdrive configs. Hyperdrive accelerates database connections from Workers by pooling and caching at the edge. Start here to discover config IDs.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_hyperdrive_config", + "description": "Get a Hyperdrive config's details: origin connection, caching, mTLS. Use after list_hyperdrive_configs.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "hyperdrive_id": { + "type": "string", + "description": "Hyperdrive config identifier" + } + }, + "required": [ + "hyperdrive_id" + ] + } + }, + { + "name": "cloudflare_create_hyperdrive_config", + "description": "Create a Hyperdrive config that proxies and caches a database connection (Postgres, MySQL) for Workers.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "caching": { + "type": "string", + "description": "Optional caching JSON: {disabled, max_age, stale_while_revalidate}" + }, + "mtls": { + "type": "string", + "description": "Optional mTLS JSON" + }, + "name": { + "type": "string", + "description": "Config name" + }, + "origin": { + "type": "string", + "description": "Origin connection JSON: {scheme, host, port, database, user, password}" + } + }, + "required": [ + "name", + "origin" + ] + } + }, + { + "name": "cloudflare_delete_hyperdrive_config", + "description": "Delete a Hyperdrive config. Workers using this config will fail until rebound.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "hyperdrive_id": { + "type": "string", + "description": "Hyperdrive config identifier" + } + }, + "required": [ + "hyperdrive_id" + ] + } + }, + { + "name": "cloudflare_list_worker_secrets", + "description": "List secret names bound to a Worker script. Values are never returned by the API. Use to audit which secrets a Worker has access to.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "script_name": { + "type": "string", + "description": "Worker script name" + } + }, + "required": [ + "script_name" + ] + } + }, + { + "name": "cloudflare_list_worker_deployments", + "description": "List recent deployments for a Worker script with versions, authors, timestamps. Use to inspect deploy history or find a version to roll back to.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "script_name": { + "type": "string", + "description": "Worker script name" + } + }, + "required": [ + "script_name" + ] + } + }, + { + "name": "cloudflare_get_worker_subdomain", + "description": "Get the workers.dev subdomain enabled for this account (used as the default Worker URL).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_list_worker_tails", + "description": "List active Worker tails (live log streams) for a script. Use to find existing tails before opening a new live-log session.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "script_name": { + "type": "string", + "description": "Worker script name" + } + }, + "required": [ + "script_name" + ] + } + }, + { + "name": "cloudflare_create_pages_project", + "description": "Create a Cloudflare Pages project (static site / Jamstack app).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "build_config": { + "type": "string", + "description": "Optional build config JSON: {build_command, destination_dir, root_dir, web_analytics_tag}" + }, + "deployment_configs": { + "type": "string", + "description": "Optional deployment_configs JSON: {production, preview}" + }, + "name": { + "type": "string", + "description": "Project name" + }, + "production_branch": { + "type": "string", + "description": "Production branch (e.g. main)" + }, + "source": { + "type": "string", + "description": "Optional source JSON: {type, config: {owner, repo_name, production_branch, ...}}" + } + }, + "required": [ + "name", + "production_branch" + ] + } + }, + { + "name": "cloudflare_create_pages_deployment", + "description": "Trigger a new Pages deployment, optionally targeting a specific branch.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "branch": { + "type": "string", + "description": "Optional branch to deploy (defaults to production)" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "project_name" + ] + } + }, + { + "name": "cloudflare_list_pages_domains", + "description": "List custom domains attached to a Pages project (DNS + cert state).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "project_name": { + "type": "string", + "description": "Pages project name" + } + }, + "required": [ + "project_name" + ] + } + }, + { + "name": "cloudflare_bulk_delete_kv_values", + "description": "Delete multiple keys from a Workers KV namespace in one call (up to 10000 keys).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "keys": { + "type": "string", + "description": "JSON array of key names to delete" + }, + "namespace_id": { + "type": "string", + "description": "KV namespace identifier" + } + }, + "required": [ + "keys", + "namespace_id" + ] + } + }, + { + "name": "cloudflare_list_stream_videos", + "description": "List videos hosted on Cloudflare Stream. Start here to discover video IDs for playback URLs, analytics, or deletion.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "after": { + "type": "string", + "description": "Show videos uploaded after this ISO 8601 timestamp" + }, + "before": { + "type": "string", + "description": "Show videos uploaded before this ISO 8601 timestamp" + }, + "creator": { + "type": "string", + "description": "Filter by creator id" + }, + "search": { + "type": "string", + "description": "Free-text search across video names" + }, + "status": { + "type": "string", + "description": "Filter by status (queued, inprogress, ready, error)" + } + } + } + }, + { + "name": "cloudflare_get_stream_video", + "description": "Get a Stream video's metadata: playback URLs (HLS/DASH), thumbnail, duration, status. Use after list_stream_videos.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "identifier": { + "type": "string", + "description": "Stream video identifier" + } + }, + "required": [ + "identifier" + ] + } + }, + { + "name": "cloudflare_delete_stream_video", + "description": "Delete a Stream video. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "identifier": { + "type": "string", + "description": "Stream video identifier" + } + }, + "required": [ + "identifier" + ] + } + }, + { + "name": "cloudflare_list_images", + "description": "List images hosted on Cloudflare Images. Start here to discover image IDs for delivery URLs or deletion.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 50)" + } + } + } + }, + { + "name": "cloudflare_get_image", + "description": "Get a Cloudflare Images record: variants, metadata, upload timestamp.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "image_id": { + "type": "string", + "description": "Image identifier" + } + }, + "required": [ + "image_id" + ] + } + }, + { + "name": "cloudflare_delete_image", + "description": "Delete an image from Cloudflare Images. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "image_id": { + "type": "string", + "description": "Image identifier" + } + }, + "required": [ + "image_id" + ] + } + }, + { + "name": "cloudflare_list_access_apps", + "description": "List Cloudflare Zero Trust Access applications (apps protected by Cloudflare Access). Start here to discover app IDs before listing policies or auditing access.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 25)" + } + } + } + }, + { + "name": "cloudflare_list_access_app_policies", + "description": "List Access policies bound to a specific application. Shows who can access the app (groups, emails, IPs, IdPs, mTLS). Use after list_access_apps.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "app_id": { + "type": "string", + "description": "Access application identifier" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "cloudflare_list_access_identity_providers", + "description": "List configured Access identity providers (Google, Okta, Azure AD, SAML, OIDC, GitHub, etc.).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_list_tunnels", + "description": "List Cloudflared tunnels for an account. Tunnels expose private origins to Cloudflare without inbound ports. Start here to discover tunnel IDs and health.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "include_deleted": { + "type": "string", + "description": "Include deleted tunnels (true/false)" + }, + "name": { + "type": "string", + "description": "Filter by tunnel name" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + }, + "status": { + "type": "string", + "description": "Filter by status: healthy, degraded, down, inactive" + } + } + } + }, + { + "name": "cloudflare_get_tunnel", + "description": "Get a Cloudflared tunnel's details: name, status, connections, created/deleted timestamps. Use after list_tunnels.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "tunnel_id": { + "type": "string", + "description": "Tunnel identifier" + } + }, + "required": [ + "tunnel_id" + ] + } + }, + { + "name": "cloudflare_delete_tunnel", + "description": "Delete a Cloudflared tunnel. Origins behind it become unreachable.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "tunnel_id": { + "type": "string", + "description": "Tunnel identifier" + } + }, + "required": [ + "tunnel_id" + ] + } + }, + { + "name": "cloudflare_list_email_routing_rules", + "description": "List Email Routing rules for a zone (which incoming addresses forward where). Start here for email routing config audits.", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_email_routing_addresses", + "description": "List verified destination email addresses for Email Routing (account-scoped).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + }, + "verified": { + "type": "string", + "description": "Filter by verified status (true/false)" + } + } + } + }, + { + "name": "cloudflare_get_email_routing_settings", + "description": "Get Email Routing settings for a zone (enabled state, MX/SPF records, skip_wizard).", + "inputSchema": { + "type": "object", + "properties": { + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_logpush_jobs", + "description": "List Logpush jobs for an account (HTTP requests, firewall events, Workers traces, etc. exported to S3/GCS/Azure/Sumo/Datadog/Splunk/New Relic/R2). Start here to inspect log-export pipelines.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_get_logpush_job", + "description": "Get a Logpush job's config: dataset, destination, frequency, filters, last error. Use after list_logpush_jobs.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "job_id": { + "type": "string", + "description": "Logpush job ID (integer)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "cloudflare_create_logpush_job", + "description": "Create a Logpush job to export Cloudflare logs to an external sink (S3, GCS, Azure, R2, Splunk, Datadog, New Relic, Sumo Logic, HTTP).", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + }, + "dataset": { + "type": "string", + "description": "Log dataset (e.g. http_requests, firewall_events, workers_trace_events)" + }, + "destination_conf": { + "type": "string", + "description": "Destination connection string (e.g. s3://bucket/path?region=...)" + }, + "enabled": { + "type": "string", + "description": "Whether the job is enabled (true/false)" + }, + "filter": { + "type": "string", + "description": "Optional log filter expression" + }, + "frequency": { + "type": "string", + "description": "Optional frequency: high or low" + }, + "logpull_options": { + "type": "string", + "description": "Optional logpull options string (fields, timestamps, etc.)" + }, + "name": { + "type": "string", + "description": "Optional job name" + }, + "output_options": { + "type": "string", + "description": "Optional output options map (field_names, timestamp_format, batch sizing)" + } + }, + "required": [ + "dataset", + "destination_conf" + ] + } + }, + { + "name": "cloudflare_list_page_rules", + "description": "List Page Rules for a zone (URL-pattern rules for cache, redirects, headers, security). Start here for legacy page-rule audits before migrating to Rulesets.", + "inputSchema": { + "type": "object", + "properties": { + "direction": { + "type": "string", + "description": "Sort direction (asc, desc)" + }, + "order": { + "type": "string", + "description": "Sort field (priority, status)" + }, + "status": { + "type": "string", + "description": "Filter by status (active, disabled)" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "zone_id" + ] + } + }, + { + "name": "cloudflare_get_page_rule", + "description": "Get a Page Rule's targets and actions. Use after list_page_rules.", + "inputSchema": { + "type": "object", + "properties": { + "pagerule_id": { + "type": "string", + "description": "Page Rule identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "pagerule_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_delete_page_rule", + "description": "Delete a Page Rule.", + "inputSchema": { + "type": "object", + "properties": { + "pagerule_id": { + "type": "string", + "description": "Page Rule identifier" + }, + "zone_id": { + "type": "string", + "description": "Zone identifier" + } + }, + "required": [ + "pagerule_id", + "zone_id" + ] + } + }, + { + "name": "cloudflare_list_notification_policies", + "description": "List notification (alerting) policies — what conditions trigger emails, PagerDuty, webhooks for an account.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_list_notification_webhooks", + "description": "List configured webhook destinations for Cloudflare notifications.", + "inputSchema": { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "Account identifier (defaults to configured account_id)" + } + } + } + }, + { + "name": "cloudflare_list_api_tokens", + "description": "List Cloudflare API tokens issued under the authenticated user. Start here to audit which tokens exist, their scopes, and their last-used timestamp.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number (default 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default 20)" + } + } + } + }, + { + "name": "cloudflare_get_api_token", + "description": "Get a Cloudflare API token's policy details: permissions, IP/time restrictions, expiry, last_used_on. Use after list_api_tokens.", + "inputSchema": { + "type": "object", + "properties": { + "token_id": { + "type": "string", + "description": "API token identifier" + } + }, + "required": [ + "token_id" + ] + } + }, + { + "name": "cloudflare_delete_api_token", + "description": "Revoke (delete) a Cloudflare API token. Any client using it will start receiving 401s.", + "inputSchema": { + "type": "object", + "properties": { + "token_id": { + "type": "string", + "description": "API token identifier" + } + }, + "required": [ + "token_id" + ] + } + }, + { + "name": "digitalocean_get_account", + "description": "Get current account information including email, droplet limit, and status. Start here to verify access.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "digitalocean_list_droplets", + "description": "List all droplets in the account", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page (max 200)" + }, + "tag_name": { + "type": "string", + "description": "Filter by tag" + } + } + } + }, + { + "name": "digitalocean_get_droplet", + "description": "Get details of a specific droplet", + "inputSchema": { + "type": "object", + "properties": { + "droplet_id": { + "type": "string", + "description": "Droplet ID (integer)" + } + }, + "required": [ + "droplet_id" + ] + } + }, + { + "name": "digitalocean_create_droplet", + "description": "Create a new droplet", + "inputSchema": { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "Image slug or ID (e.g. ubuntu-24-04-x64)" + }, + "name": { + "type": "string", + "description": "Droplet name" + }, + "region": { + "type": "string", + "description": "Region slug (e.g. nyc3)" + }, + "size": { + "type": "string", + "description": "Size slug (e.g. s-1vcpu-1gb)" + }, + "ssh_keys": { + "type": "string", + "description": "Comma-separated SSH key IDs or fingerprints" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags" + }, + "vpc_uuid": { + "type": "string", + "description": "VPC UUID" + } + }, + "required": [ + "image", + "name", + "region", + "size" + ] + } + }, + { + "name": "digitalocean_delete_droplet", + "description": "Delete a droplet by ID", + "inputSchema": { + "type": "object", + "properties": { + "droplet_id": { + "type": "string", + "description": "Droplet ID (integer)" + } + }, + "required": [ + "droplet_id" + ] + } + }, + { + "name": "digitalocean_reboot_droplet", + "description": "Reboot a droplet", + "inputSchema": { + "type": "object", + "properties": { + "droplet_id": { + "type": "string", + "description": "Droplet ID (integer)" + } + }, + "required": [ + "droplet_id" + ] + } + }, + { + "name": "digitalocean_poweroff_droplet", + "description": "Power off a droplet (hard shutdown)", + "inputSchema": { + "type": "object", + "properties": { + "droplet_id": { + "type": "string", + "description": "Droplet ID (integer)" + } + }, + "required": [ + "droplet_id" + ] + } + }, + { + "name": "digitalocean_poweron_droplet", + "description": "Power on a droplet", + "inputSchema": { + "type": "object", + "properties": { + "droplet_id": { + "type": "string", + "description": "Droplet ID (integer)" + } + }, + "required": [ + "droplet_id" + ] + } + }, + { + "name": "digitalocean_list_kubernetes_clusters", + "description": "List all Kubernetes clusters", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_kubernetes_cluster", + "description": "Get details of a specific Kubernetes cluster", + "inputSchema": { + "type": "object", + "properties": { + "cluster_id": { + "type": "string", + "description": "Cluster UUID" + } + }, + "required": [ + "cluster_id" + ] + } + }, + { + "name": "digitalocean_list_kubernetes_node_pools", + "description": "List node pools for a Kubernetes cluster", + "inputSchema": { + "type": "object", + "properties": { + "cluster_id": { + "type": "string", + "description": "Cluster UUID" + } + }, + "required": [ + "cluster_id" + ] + } + }, + { + "name": "digitalocean_list_databases", + "description": "List all managed database clusters", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_database", + "description": "Get details of a managed database cluster", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database cluster UUID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "digitalocean_list_database_dbs", + "description": "List databases within a managed database cluster", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database cluster UUID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "digitalocean_list_database_users", + "description": "List users for a managed database cluster", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database cluster UUID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "digitalocean_list_database_pools", + "description": "List connection pools for a managed database cluster", + "inputSchema": { + "type": "object", + "properties": { + "database_id": { + "type": "string", + "description": "Database cluster UUID" + } + }, + "required": [ + "database_id" + ] + } + }, + { + "name": "digitalocean_list_domains", + "description": "List all domains", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_domain", + "description": "Get details of a domain", + "inputSchema": { + "type": "object", + "properties": { + "domain_name": { + "type": "string", + "description": "Domain name (e.g. example.com)" + } + }, + "required": [ + "domain_name" + ] + } + }, + { + "name": "digitalocean_list_domain_records", + "description": "List DNS records for a domain", + "inputSchema": { + "type": "object", + "properties": { + "domain_name": { + "type": "string", + "description": "Domain name" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "domain_name" + ] + } + }, + { + "name": "digitalocean_list_load_balancers", + "description": "List all load balancers", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_load_balancer", + "description": "Get details of a load balancer", + "inputSchema": { + "type": "object", + "properties": { + "lb_id": { + "type": "string", + "description": "Load balancer UUID" + } + }, + "required": [ + "lb_id" + ] + } + }, + { + "name": "digitalocean_list_firewalls", + "description": "List all cloud firewalls", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_firewall", + "description": "Get details of a cloud firewall", + "inputSchema": { + "type": "object", + "properties": { + "firewall_id": { + "type": "string", + "description": "Firewall UUID" + } + }, + "required": [ + "firewall_id" + ] + } + }, + { + "name": "digitalocean_list_vpcs", + "description": "List all VPCs", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_vpc", + "description": "Get details of a VPC", + "inputSchema": { + "type": "object", + "properties": { + "vpc_id": { + "type": "string", + "description": "VPC UUID" + } + }, + "required": [ + "vpc_id" + ] + } + }, + { + "name": "digitalocean_list_volumes", + "description": "List all block storage volumes", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "region": { + "type": "string", + "description": "Filter by region slug" + } + } + } + }, + { + "name": "digitalocean_get_volume", + "description": "Get details of a block storage volume", + "inputSchema": { + "type": "object", + "properties": { + "volume_id": { + "type": "string", + "description": "Volume UUID" + } + }, + "required": [ + "volume_id" + ] + } + }, + { + "name": "digitalocean_list_apps", + "description": "List all App Platform apps", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_app", + "description": "Get details of an App Platform app including its spec and active deployment", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_delete_app", + "description": "Delete an App Platform app", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_restart_app", + "description": "Restart an App Platform app, optionally targeting specific components", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + }, + "components": { + "type": "string", + "description": "Comma-separated component names to restart (omit to restart all)" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_list_app_deployments", + "description": "List deployments for an App Platform app", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + }, + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_get_app_deployment", + "description": "Get details of a specific App Platform deployment", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + }, + "deployment_id": { + "type": "string", + "description": "Deployment UUID" + } + }, + "required": [ + "app_id", + "deployment_id" + ] + } + }, + { + "name": "digitalocean_create_app_deployment", + "description": "Trigger a new deployment for an App Platform app", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + }, + "force_build": { + "type": "string", + "description": "Force rebuild even if no source changes (true/false)" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_get_app_logs", + "description": "Get logs for an App Platform app. Use log_type BUILD for build logs, DEPLOY for deploy logs, or RUN for runtime logs", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + }, + "component": { + "type": "string", + "description": "Component name (omit for all components)" + }, + "deployment_id": { + "type": "string", + "description": "Deployment UUID (omit for active deployment)" + }, + "log_type": { + "type": "string", + "description": "Log type: BUILD, DEPLOY, RUN, RUN_RESTARTED, or AUTOSCALE_EVENT" + }, + "tail_lines": { + "type": "string", + "description": "Number of log lines to return (default 100)" + } + }, + "required": [ + "app_id", + "log_type" + ] + } + }, + { + "name": "digitalocean_get_app_health", + "description": "Get health status of all components in an App Platform app", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_list_app_alerts", + "description": "List alerts configured for an App Platform app", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "App UUID" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "digitalocean_list_regions", + "description": "List all available regions", + "inputSchema": { + "type": "object" + } + }, + { + "name": "digitalocean_list_sizes", + "description": "List all available droplet sizes", + "inputSchema": { + "type": "object" + } + }, + { + "name": "digitalocean_list_images", + "description": "List available images (OS distributions and snapshots)", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "type": { + "type": "string", + "description": "Filter by type: distribution, application, or user" + } + } + } + }, + { + "name": "digitalocean_list_ssh_keys", + "description": "List all SSH keys in the account", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_list_snapshots", + "description": "List all snapshots (droplet and volume)", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "resource_type": { + "type": "string", + "description": "Filter: droplet or volume" + } + } + } + }, + { + "name": "digitalocean_list_projects", + "description": "List all projects", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_get_project", + "description": "Get details of a project", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Project UUID" + } + }, + "required": [ + "project_id" + ] + } + }, + { + "name": "digitalocean_get_balance", + "description": "Get current account balance", + "inputSchema": { + "type": "object" + } + }, + { + "name": "digitalocean_list_invoices", + "description": "List billing invoices", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_list_cdn_endpoints", + "description": "List all CDN endpoints", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_list_certificates", + "description": "List all SSL/TLS certificates", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "digitalocean_list_registries", + "description": "List container registry repositories", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + }, + "registry_name": { + "type": "string", + "description": "Registry name" + } + }, + "required": [ + "registry_name" + ] + } + }, + { + "name": "digitalocean_list_tags", + "description": "List all tags", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number" + }, + "per_page": { + "type": "string", + "description": "Results per page" + } + } + } + }, + { + "name": "fly_list_apps", + "description": "List all Fly.io apps in an organization. Start here for most workflows — returns app names needed by other tools", + "inputSchema": { + "type": "object", + "properties": { + "org_slug": { + "type": "string", + "description": "Organization slug (e.g. 'personal')" + } + }, + "required": [ + "org_slug" + ] + } + }, + { + "name": "fly_get_app", + "description": "Get details of a Fly.io app including status and organization info", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + } + }, + "required": [ + "app_name" + ] + } + }, + { + "name": "fly_create_app", + "description": "Create a new Fly.io app in an organization", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "Name for the new app" + }, + "network": { + "type": "string", + "description": "Optional IPv6 private network name to segment the app onto" + }, + "org_slug": { + "type": "string", + "description": "Organization slug (e.g. 'personal')" + } + }, + "required": [ + "app_name", + "org_slug" + ] + } + }, + { + "name": "fly_delete_app", + "description": "Delete a Fly.io app. Use force=true to stop all Machines first", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name to delete" + }, + "force": { + "type": "string", + "description": "Force stop all Machines and delete immediately (true/false)" + } + }, + "required": [ + "app_name" + ] + } + }, + { + "name": "fly_list_machines", + "description": "List all Machines in a Fly.io app. Returns IDs, state, region, and image info. Use summary=true for lighter response", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "include_deleted": { + "type": "string", + "description": "Include deleted machines (true/false)" + }, + "region": { + "type": "string", + "description": "Filter by region code (e.g. 'ord', 'iad')" + }, + "state": { + "type": "string", + "description": "Comma-separated states to filter: created, started, stopped, suspended" + }, + "summary": { + "type": "string", + "description": "Only return summary info, omit config/checks/events (true/false)" + } + }, + "required": [ + "app_name" + ] + } + }, + { + "name": "fly_get_machine", + "description": "Get full details of a specific Machine including config, events, and checks", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_create_machine", + "description": "Create a new Machine in a Fly.io app. Specify image, resources (guest), region, and optional services/mounts", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "config": { + "type": "string", + "description": "Machine config object with: image (required), guest {cpus, memory_mb, cpu_kind}, env {}, services [], mounts [], auto_destroy, restart {policy}, metadata {}" + }, + "name": { + "type": "string", + "description": "Optional machine name" + }, + "region": { + "type": "string", + "description": "Region code (e.g. 'ord', 'iad', 'lhr')" + } + }, + "required": [ + "app_name", + "config" + ] + } + }, + { + "name": "fly_update_machine", + "description": "Update a Machine's configuration (image, resources, env, services, etc.)", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "config": { + "type": "string", + "description": "Updated machine config object" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "config", + "machine_id" + ] + } + }, + { + "name": "fly_delete_machine", + "description": "Delete a Machine. Use force=true to kill a running machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "force": { + "type": "string", + "description": "Force kill if running (true/false)" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_start_machine", + "description": "Start a stopped Machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_stop_machine", + "description": "Stop a running Machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_restart_machine", + "description": "Restart a Machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_signal_machine", + "description": "Send a Unix signal to a Machine process (e.g. SIGTERM, SIGKILL, SIGHUP)", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + }, + "signal": { + "type": "string", + "description": "Signal name (SIGTERM, SIGKILL, SIGHUP, SIGUSR1, SIGUSR2, etc.)" + } + }, + "required": [ + "app_name", + "machine_id", + "signal" + ] + } + }, + { + "name": "fly_wait_machine", + "description": "Wait for a Machine to reach a specific state. Blocks until state is reached or timeout", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + }, + "state": { + "type": "string", + "description": "Target state: started, stopped, suspended, destroyed (default: started)" + }, + "timeout": { + "type": "string", + "description": "Timeout in seconds (default: 60)" + } + }, + "required": [ + "app_name", + "machine_id" + ] + } + }, + { + "name": "fly_exec_machine", + "description": "Execute a command inside a running Machine and return stdout/stderr", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "command": { + "type": "string", + "description": "Command to execute as an array of strings (e.g. [\"ls\", \"-la\"])" + }, + "machine_id": { + "type": "string", + "description": "Machine ID" + } + }, + "required": [ + "app_name", + "command", + "machine_id" + ] + } + }, + { + "name": "fly_list_volumes", + "description": "List all persistent volumes attached to a Fly.io app", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + } + }, + "required": [ + "app_name" + ] + } + }, + { + "name": "fly_get_volume", + "description": "Get details of a specific volume including size, region, and attached machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "volume_id": { + "type": "string", + "description": "Volume ID" + } + }, + "required": [ + "app_name", + "volume_id" + ] + } + }, + { + "name": "fly_create_volume", + "description": "Create a persistent volume in a Fly.io app. Volumes are region-specific", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "auto_backup_enabled": { + "type": "string", + "description": "Enable automatic backups (true/false)" + }, + "encrypted": { + "type": "string", + "description": "Encrypt the volume (true/false, default: true)" + }, + "name": { + "type": "string", + "description": "Volume name" + }, + "region": { + "type": "string", + "description": "Region code (e.g. 'ord')" + }, + "size_gb": { + "type": "string", + "description": "Size in GB (default: 1)" + }, + "snapshot_retention": { + "type": "string", + "description": "Number of snapshots to retain" + } + }, + "required": [ + "app_name", + "name", + "region" + ] + } + }, + { + "name": "fly_update_volume", + "description": "Update a volume's size or snapshot settings", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "auto_backup_enabled": { + "type": "string", + "description": "Enable automatic backups (true/false)" + }, + "snapshot_retention": { + "type": "string", + "description": "Number of snapshots to retain" + }, + "volume_id": { + "type": "string", + "description": "Volume ID" + } + }, + "required": [ + "app_name", + "volume_id" + ] + } + }, + { + "name": "fly_delete_volume", + "description": "Delete a persistent volume. Volume must not be attached to a running machine", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "volume_id": { + "type": "string", + "description": "Volume ID" + } + }, + "required": [ + "app_name", + "volume_id" + ] + } + }, + { + "name": "fly_list_volume_snapshots", + "description": "List snapshots for a specific volume", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "volume_id": { + "type": "string", + "description": "Volume ID" + } + }, + "required": [ + "app_name", + "volume_id" + ] + } + }, + { + "name": "fly_list_secrets", + "description": "List all secrets for a Fly.io app. Returns names and digests only — values are never exposed", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + } + }, + "required": [ + "app_name" + ] + } + }, + { + "name": "fly_set_secrets", + "description": "Set one or more secrets on a Fly.io app. Machines will be restarted to pick up changes", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "secrets": { + "type": "string", + "description": "Key-value map of secrets to set (e.g. {\"DATABASE_URL\": \"postgres://...\"})" + } + }, + "required": [ + "app_name", + "secrets" + ] + } + }, + { + "name": "fly_unset_secrets", + "description": "Remove one or more secrets from a Fly.io app. Machines will be restarted", + "inputSchema": { + "type": "object", + "properties": { + "app_name": { + "type": "string", + "description": "App name" + }, + "keys": { + "type": "string", + "description": "Array of secret names to remove" + } + }, + "required": [ + "app_name", + "keys" + ] + } + }, + { + "name": "snowflake_execute_query", + "description": "Execute a SQL query against a Snowflake data warehouse and return results as JSON rows. Supports SELECT, SHOW, DESCRIBE, DDL, and DML statements", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database context (overrides configured default)" + }, + "query": { + "type": "string", + "description": "SQL statement to execute" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema context (overrides configured default)" + }, + "timeout": { + "type": "string", + "description": "Query timeout in seconds (default: 60)" + }, + "warehouse": { + "type": "string", + "description": "Warehouse to use (overrides configured default)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "snowflake_get_query_status", + "description": "Check the status of an async Snowflake query and retrieve results when complete. Use the statement handle returned from snowflake_execute_query", + "inputSchema": { + "type": "object", + "properties": { + "partition": { + "type": "string", + "description": "Partition number to fetch for large result sets (0-based)" + }, + "statement_handle": { + "type": "string", + "description": "UUID statement handle from a previous query submission" + } + }, + "required": [ + "statement_handle" + ] + } + }, + { + "name": "snowflake_cancel_query", + "description": "Cancel a running Snowflake query by its statement handle", + "inputSchema": { + "type": "object", + "properties": { + "statement_handle": { + "type": "string", + "description": "UUID statement handle of the query to cancel" + } + }, + "required": [ + "statement_handle" + ] + } + }, + { + "name": "snowflake_list_databases", + "description": "List all databases accessible in the Snowflake account. Start here for schema discovery.", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_list_schemas", + "description": "List all schemas in a Snowflake database", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_list_tables", + "description": "List tables in a Snowflake database/schema with row counts and sizes", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_list_views", + "description": "List views in a Snowflake database/schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_describe_table", + "description": "Describe a table's columns with names, types, and constraints in Snowflake", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "snowflake_show_create_table", + "description": "Show the DDL CREATE statement for a Snowflake table", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + }, + "table": { + "type": "string", + "description": "Table name" + } + }, + "required": [ + "table" + ] + } + }, + { + "name": "snowflake_list_warehouses", + "description": "List all warehouses in the Snowflake account with state, size, and cluster info", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_list_running_queries", + "description": "List currently running and recently completed queries in Snowflake", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Maximum number of queries to return (default: 50)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_current_session", + "description": "Get current Snowflake session info including user, role, warehouse, and database", + "inputSchema": { + "type": "object" + } + }, + { + "name": "snowflake_list_users", + "description": "List all users in the Snowflake account", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_list_roles", + "description": "List all roles in the Snowflake account", + "inputSchema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + } + } + } + }, + { + "name": "snowflake_list_stages", + "description": "List stages in a Snowflake database/schema for data loading", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_list_tasks", + "description": "List tasks (scheduled SQL jobs) in a Snowflake database/schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_list_pipes", + "description": "List Snowpipe definitions for continuous data ingestion", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_list_streams", + "description": "List streams (change data capture) in a Snowflake database/schema", + "inputSchema": { + "type": "object", + "properties": { + "database": { + "type": "string", + "description": "Database name (defaults to configured database)" + }, + "role": { + "type": "string", + "description": "Role to use (overrides configured default)" + }, + "schema": { + "type": "string", + "description": "Schema name (defaults to configured schema)" + } + } + } + }, + { + "name": "snowflake_cortex_analyst", + "description": "Ask a natural-language question against a Snowflake Cortex Analyst semantic layer. Returns generated SQL, an explanation, and follow-up suggestions. Use snowflake_execute_query to run the returned SQL", + "inputSchema": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Natural-language question to ask (e.g. 'What were our top 10 products by revenue last quarter?')" + }, + "semantic_model": { + "type": "string", + "description": "Inline semantic model YAML (alternative to semantic_model_file and semantic_view)" + }, + "semantic_model_file": { + "type": "string", + "description": "Stage path to a semantic model YAML (e.g. @MY_DB.MY_SCHEMA.MY_STAGE/model.yaml)" + }, + "semantic_view": { + "type": "string", + "description": "Fully qualified semantic view name (overrides configured default)" + } + }, + "required": [ + "question" + ] + } + }, + { + "name": "acp_list_agents", + "description": "List available remote agents on ACP servers. Start here to discover AI agents, bots, and autonomous workers before invoking them. Returns agent names, descriptions, and capabilities", + "inputSchema": { + "type": "object", + "properties": { + "server": { + "type": "string", + "description": "Name of a pre-configured ACP server to query. Uses the first configured server if omitted" + }, + "server_headers": { + "type": "string", + "description": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})" + }, + "server_url": { + "type": "string", + "description": "URL of an ACP server to query directly (e.g. http://localhost:8199). Overrides server name lookup" + } + } + } + }, + { + "name": "acp_run_agent", + "description": "Invoke a remote ACP agent with a message and get its response. Send a text prompt to an AI agent on a remote server. Use acp_list_agents first to discover available agents. If the agent enters an awaiting state (needs more input), the response includes a run_id — use acp_resume_run to continue", + "inputSchema": { + "type": "object", + "properties": { + "agent_name": { + "type": "string", + "description": "Name of the remote agent to invoke" + }, + "input": { + "type": "string", + "description": "Text message to send to the agent" + }, + "server": { + "type": "string", + "description": "Name of a pre-configured ACP server. Uses the first configured server if omitted" + }, + "server_headers": { + "type": "string", + "description": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})" + }, + "server_url": { + "type": "string", + "description": "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup" + }, + "session_id": { + "type": "string", + "description": "Session ID for multi-turn conversations with the same agent" + } + }, + "required": [ + "agent_name", + "input" + ] + } + }, + { + "name": "acp_resume_run", + "description": "Resume an ACP agent run that is waiting for additional input. When acp_run_agent returns a response indicating the agent is awaiting input, use this tool to provide the requested information and continue the run", + "inputSchema": { + "type": "object", + "properties": { + "input": { + "type": "string", + "description": "Text response to provide to the awaiting agent" + }, + "run_id": { + "type": "string", + "description": "The run_id returned by acp_run_agent when the agent entered awaiting state" + }, + "server": { + "type": "string", + "description": "Name of a pre-configured ACP server. Uses the first configured server if omitted" + }, + "server_headers": { + "type": "string", + "description": "Optional JSON object of HTTP headers to send with the request (e.g. {\"Authorization\":\"Bearer sk-xxx\"})" + }, + "server_url": { + "type": "string", + "description": "URL of an ACP server to connect to directly (e.g. http://localhost:8199). Overrides server name lookup" + } + }, + "required": [ + "input", + "run_id" + ] + } + }, + { + "name": "agents_project_list", + "description": "List all registered projects via gRPC ProjectService.ListProjects. Returns Project objects with name, repo, branch, and agent templates.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "agents_project_register", + "description": "Register a new project via gRPC ProjectService.RegisterProject. A project defines a git repo and agent templates for spawning.", + "inputSchema": { + "type": "object", + "properties": { + "agents": { + "type": "string", + "description": "JSON array of AgentTemplate objects. Each has: name (required), command (required), port_env, capabilities (string array), a2a_card_config ({name, description, skills, input_modes, output_modes, streaming})" + }, + "branch": { + "type": "string", + "description": "Default branch for new workspaces (default: main)" + }, + "name": { + "type": "string", + "description": "Unique project name" + }, + "repo": { + "type": "string", + "description": "Path or URL to the git repository" + } + }, + "required": [ + "name", + "repo" + ] + } + }, + { + "name": "agents_project_unregister", + "description": "Unregister a project via gRPC ProjectService.UnregisterProject. Fails if active workspaces with running agents exist.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Project name to unregister" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "agents_workspace_create", + "description": "Create a new workspace via gRPC WorkspaceService.CreateWorkspace. Creates a git worktree and optionally auto-spawns agents.", + "inputSchema": { + "type": "object", + "properties": { + "auto_agents": { + "type": "string", + "description": "JSON array of template names to auto-spawn (e.g. [\"crush\", \"reviewer\"])" + }, + "branch": { + "type": "string", + "description": "Git branch override (defaults to project's default branch)" + }, + "name": { + "type": "string", + "description": "Unique workspace name (used as worktree branch name)" + }, + "project": { + "type": "string", + "description": "Project to create workspace from (must be registered)" + } + }, + "required": [ + "name", + "project" + ] + } + }, + { + "name": "agents_workspace_list", + "description": "List workspaces via gRPC WorkspaceService.ListWorkspaces. Optionally filter by project or status.", + "inputSchema": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Filter by project name" + }, + "status": { + "type": "string", + "description": "Filter by status: active or inactive" + } + } + } + }, + { + "name": "agents_workspace_get", + "description": "Get workspace details via gRPC WorkspaceService.GetWorkspace. Returns agents, directory path, and creation time.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Workspace name" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "agents_workspace_destroy", + "description": "Destroy a workspace via gRPC WorkspaceService.DestroyWorkspace. Stops all agents, cancels working tasks, and optionally removes the worktree.", + "inputSchema": { + "type": "object", + "properties": { + "keep_worktree": { + "type": "string", + "description": "If true, preserve the git worktree directory on disk (default: false)" + }, + "name": { + "type": "string", + "description": "Workspace name to destroy" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "agents_agent_spawn", + "description": "Spawn a new A2A agent via gRPC AgentService.SpawnAgent. Returns AgentInstance with id, port, direct_url, proxy_url, and status.", + "inputSchema": { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "JSON object of additional environment variables" + }, + "name": { + "type": "string", + "description": "Custom instance name (defaults to template name)" + }, + "permission": { + "type": "string", + "description": "Permission level: session, project, or admin" + }, + "prompt": { + "type": "string", + "description": "Initial prompt to send after agent reaches READY" + }, + "scope": { + "type": "string", + "description": "JSON Scope object: {\"global\": false, \"projects\": [\"proj-a\"]}" + }, + "template": { + "type": "string", + "description": "Agent template name from the project" + }, + "workspace": { + "type": "string", + "description": "Workspace name to spawn the agent in" + } + }, + "required": [ + "template", + "workspace" + ] + } + }, + { + "name": "agents_agent_list", + "description": "List agent instances via gRPC AgentService.ListAgents. Optionally filter by workspace, status, or template.", + "inputSchema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "Filter by status: starting, ready, busy, error, stopping, stopped" + }, + "template": { + "type": "string", + "description": "Filter by template name" + }, + "workspace": { + "type": "string", + "description": "Filter by workspace name" + } + } + } + }, + { + "name": "agents_agent_status", + "description": "Get agent status via gRPC AgentService.GetAgentStatus. Returns AgentInstance with resolved A2A AgentCard.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID" + } + }, + "required": [ + "agent_id" + ] + } + }, + { + "name": "agents_agent_stop", + "description": "Stop an agent via gRPC AgentService.StopAgent. Cancels working A2A tasks, sends SIGTERM, waits grace period, then SIGKILL.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID to stop" + }, + "grace_period_ms": { + "type": "string", + "description": "Milliseconds to wait after SIGTERM before SIGKILL (default: 5000)" + } + }, + "required": [ + "agent_id" + ] + } + }, + { + "name": "agents_agent_restart", + "description": "Restart an agent via gRPC AgentService.RestartAgent. Stop + spawn with same config. proxy_url stays stable.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID to restart" + } + }, + "required": [ + "agent_id" + ] + } + }, + { + "name": "agents_agent_message", + "description": "Send a message to an agent via gRPC AgentService.SendAgentMessage. When blocking=true (default), polls until the task completes. When blocking=false, returns the task immediately for async tracking via agents_agent_task_status.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID" + }, + "blocking": { + "type": "string", + "description": "If true (default), wait for response. If false, return immediately." + }, + "context_id": { + "type": "string", + "description": "A2A context_id for multi-turn conversations" + }, + "message": { + "type": "string", + "description": "Text message to send" + } + }, + "required": [ + "agent_id", + "message" + ] + } + }, + { + "name": "agents_agent_task", + "description": "Create a task on an agent via gRPC AgentService.CreateAgentTask. Returns immediately with a Task for async tracking via agents_agent_task_status (never blocks on completion).", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID" + }, + "context_id": { + "type": "string", + "description": "A2A context_id to continue a conversation" + }, + "message": { + "type": "string", + "description": "Task description" + } + }, + "required": [ + "agent_id", + "message" + ] + } + }, + { + "name": "agents_agent_task_status", + "description": "Get task status via gRPC AgentService.GetAgentTaskStatus. Returns Task with status, artifacts, and history.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent instance ID" + }, + "history_length": { + "type": "string", + "description": "Maximum number of recent messages to include (default: 10)" + }, + "task_id": { + "type": "string", + "description": "A2A task ID to check" + } + }, + "required": [ + "agent_id", + "task_id" + ] + } + }, + { + "name": "agents_discover", + "description": "Discover agents via gRPC DiscoveryService.DiscoverAgents. Returns enriched AgentCards. Supports local/network scope and capability filtering.", + "inputSchema": { + "type": "object", + "properties": { + "capability": { + "type": "string", + "description": "Filter by AgentSkill tag (e.g. \"coding\")" + }, + "scope": { + "type": "string", + "description": "Discovery scope: local (default) or network" + }, + "urls": { + "type": "string", + "description": "JSON array of base URLs to probe for AgentCards (network scope)" + } + } + } + }, + { + "name": "agents_proxy_list", + "description": "List all A2A AgentCards via the ARP HTTP proxy at /a2a/agents. Returns cards for READY/BUSY agents with metadata.arp fields.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "agents_agent_card", + "description": "Get an enriched A2A AgentCard via the ARP HTTP proxy. Includes metadata.arp and supportedInterfaces pointing to the proxy.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent ID, name, or workspace/instance_name" + } + }, + "required": [ + "agent_id" + ] + } + }, + { + "name": "agents_proxy_send_message", + "description": "Send an A2A message through the ARP HTTP proxy at /a2a/agents/{id}/message:send. Routes by agent ID, name, or workspace/name.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent ID, name, or workspace/instance_name" + }, + "context_id": { + "type": "string", + "description": "A2A context_id for multi-turn conversation" + }, + "message": { + "type": "string", + "description": "Text message to send" + }, + "message_id": { + "type": "string", + "description": "Message ID (auto-generated if omitted)" + } + }, + "required": [ + "agent_id", + "message" + ] + } + }, + { + "name": "agents_proxy_get_task", + "description": "Get an A2A task via the ARP HTTP proxy.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent ID" + }, + "task_id": { + "type": "string", + "description": "A2A task ID" + } + }, + "required": [ + "agent_id", + "task_id" + ] + } + }, + { + "name": "agents_proxy_cancel_task", + "description": "Cancel an A2A task via the ARP HTTP proxy.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "type": "string", + "description": "Agent ID" + }, + "task_id": { + "type": "string", + "description": "A2A task ID to cancel" + } + }, + "required": [ + "agent_id", + "task_id" + ] + } + }, + { + "name": "agents_route_message", + "description": "Route an A2A message by skill tags via the ARP HTTP proxy at /a2a/route/message:send. Finds best matching agent (prefers READY over BUSY).", + "inputSchema": { + "type": "object", + "properties": { + "context_id": { + "type": "string", + "description": "A2A context_id for multi-turn conversation" + }, + "message": { + "type": "string", + "description": "Text message to send" + }, + "message_id": { + "type": "string", + "description": "Message ID (auto-generated if omitted)" + }, + "tags": { + "type": "string", + "description": "JSON array of skill tags to match (e.g. [\"coding\"])" + } + }, + "required": [ + "message", + "tags" + ] + } + }, + { + "name": "signoz_list_services", + "description": "List all application services and their metrics (latency, error rate, throughput). Start here for APM, service health monitoring, and performance debugging.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "start" + ] + } + }, + { + "name": "signoz_get_service_overview", + "description": "Get detailed overview metrics for a specific service over a time range. Use after list_services for latency percentiles, error rates, and request counts.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "service": { + "type": "string", + "description": "Service name" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + }, + "step": { + "type": "string", + "description": "Step interval in seconds (default: 60)" + } + }, + "required": [ + "end", + "service", + "start" + ] + } + }, + { + "name": "signoz_top_operations", + "description": "Get top operations for a service ranked by latency, error rate, or call count. Use after list_services to drill into hotspot endpoints and slow operations.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "service": { + "type": "string", + "description": "Service name" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "service", + "start" + ] + } + }, + { + "name": "signoz_top_level_operations", + "description": "Get top-level (entry point) operations across all services. Useful for finding the most called API endpoints and root spans.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "start" + ] + } + }, + { + "name": "signoz_entry_point_operations", + "description": "Get entry point operations for a service (v2). Returns the first spans in a trace for each service.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "service": { + "type": "string", + "description": "Service name" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "service", + "start" + ] + } + }, + { + "name": "signoz_search_logs", + "description": "Search and filter log entries. Supports attribute filters (severity, service, body text), ordering, and pagination. Start here for log exploration, debugging, and error investigation.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "filter": { + "type": "string", + "description": "Single filter expression: key op value, e.g. \"severity_text = 'ERROR'\". Only one expression per call." + }, + "limit": { + "type": "string", + "description": "Max logs to return (default: 20, max: 100)" + }, + "offset": { + "type": "string", + "description": "Offset for pagination (default: 0)" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "start" + ] + } + }, + { + "name": "signoz_search_traces", + "description": "Search distributed traces with filters. Find slow requests, errors, and specific operations across services. Start here for trace exploration and latency debugging.", + "inputSchema": { + "type": "object", + "properties": { + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "filter": { + "type": "string", + "description": "Single filter expression: key op value, e.g. \"hasError = true\". Only one expression per call; use service param for service filtering." + }, + "limit": { + "type": "string", + "description": "Max traces to return (default: 20, max: 100)" + }, + "offset": { + "type": "string", + "description": "Offset for pagination (default: 0)" + }, + "service": { + "type": "string", + "description": "Filter by service name" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + } + }, + "required": [ + "end", + "start" + ] + } + }, + { + "name": "signoz_get_trace", + "description": "Get all spans for a specific trace by its trace ID. Shows the full request path across services with timing breakdown. Use after search_traces.", + "inputSchema": { + "type": "object", + "properties": { + "trace_id": { + "type": "string", + "description": "Trace ID" + } + }, + "required": [ + "trace_id" + ] + } + }, + { + "name": "signoz_query_metrics", + "description": "Query time-series metrics data. Supports aggregation (avg, sum, min, max, count, rate, p50-p99), filtering, and group-by. Start here for metrics exploration, infrastructure monitoring, and custom dashboards.", + "inputSchema": { + "type": "object", + "properties": { + "aggregate_op": { + "type": "string", + "description": "Aggregation: avg, sum, min, max, count, rate, p50, p75, p90, p95, p99 (default: avg)" + }, + "end": { + "type": "string", + "description": "End time in epoch milliseconds" + }, + "filter": { + "type": "string", + "description": "Single filter expression: key op value, e.g. \"service_name = 'frontend'\". Only one expression per call." + }, + "group_by": { + "type": "string", + "description": "Comma-separated attribute keys to group by, e.g. 'service_name,host'" + }, + "metric_name": { + "type": "string", + "description": "Metric name, e.g. 'signoz_calls_total' or 'system.cpu.load_average.1m'" + }, + "start": { + "type": "string", + "description": "Start time in epoch milliseconds" + }, + "step": { + "type": "string", + "description": "Step interval in seconds (default: 60)" + } + }, + "required": [ + "end", + "metric_name", + "start" + ] + } + }, + { + "name": "signoz_list_dashboards", + "description": "List all dashboards. Start here for dashboard discovery and visualization management.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "signoz_get_dashboard", + "description": "Get a specific dashboard by ID including all panels and widget configurations. Use after list_dashboards.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "signoz_create_dashboard", + "description": "Create a new dashboard with title, description, and optional tags.", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Dashboard description" + }, + "tags": { + "type": "string", + "description": "Comma-separated tags for categorization" + }, + "title": { + "type": "string", + "description": "Dashboard title" + } + }, + "required": [ + "title" + ] + } + }, + { + "name": "signoz_update_dashboard", + "description": "Update an existing dashboard. Send dashboard content (title, widgets, layout, tags) or the full get_dashboard response — nesting is auto-corrected. The API wraps content automatically.", + "inputSchema": { + "type": "object", + "properties": { + "dashboard": { + "type": "string", + "description": "Dashboard content object with title, widgets, layout, tags, variables" + }, + "id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "dashboard", + "id" + ] + } + }, + { + "name": "signoz_delete_dashboard", + "description": "Delete a dashboard by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Dashboard ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "signoz_list_alerts", + "description": "List all alert rules including their current state (firing, inactive, pending). Start here for alert management and on-call monitoring.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "signoz_get_alert", + "description": "Get a specific alert rule by ID with full configuration and current state. Use after list_alerts.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Alert rule ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "signoz_create_alert", + "description": "Create a new alert rule. Requires a full alert rule definition JSON body.", + "inputSchema": { + "type": "object", + "properties": { + "rule": { + "type": "string", + "description": "Full alert rule definition JSON object" + } + }, + "required": [ + "rule" + ] + } + }, + { + "name": "signoz_update_alert", + "description": "Update an existing alert rule. Send the full rule object (get it first with get_alert).", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Alert rule ID" + }, + "rule": { + "type": "string", + "description": "Full alert rule definition JSON object" + } + }, + "required": [ + "id", + "rule" + ] + } + }, + { + "name": "signoz_delete_alert", + "description": "Delete an alert rule by ID.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Alert rule ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "signoz_list_saved_views", + "description": "List all saved explorer views for logs and traces. Use to find pre-configured query views.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "signoz_get_saved_view", + "description": "Get a specific saved view by ID with full query configuration. Use after list_saved_views.", + "inputSchema": { + "type": "object", + "properties": { + "view_id": { + "type": "string", + "description": "Saved view ID" + } + }, + "required": [ + "view_id" + ] + } + }, + { + "name": "signoz_create_saved_view", + "description": "Create a new saved explorer view with query configuration.", + "inputSchema": { + "type": "object", + "properties": { + "view": { + "type": "string", + "description": "Saved view definition JSON object" + } + }, + "required": [ + "view" + ] + } + }, + { + "name": "signoz_update_saved_view", + "description": "Update an existing saved view.", + "inputSchema": { + "type": "object", + "properties": { + "view": { + "type": "string", + "description": "Updated saved view definition JSON object" + }, + "view_id": { + "type": "string", + "description": "Saved view ID" + } + }, + "required": [ + "view", + "view_id" + ] + } + }, + { + "name": "signoz_delete_saved_view", + "description": "Delete a saved view by ID.", + "inputSchema": { + "type": "object", + "properties": { + "view_id": { + "type": "string", + "description": "Saved view ID" + } + }, + "required": [ + "view_id" + ] + } + }, + { + "name": "signoz_list_channels", + "description": "List all configured notification channels (Slack, email, PagerDuty, webhooks) for alerts.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "signoz_get_version", + "description": "Get SigNoz server version, edition (community/enterprise), and setup status.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "web_fetch", + "description": "Fetches the content of a URL and returns it as readable text. Use this to read documentation, API references, README files, changelogs, GitHub raw content, package docs, or any web page whose content you need to reason about. Returns extracted readable text, not raw HTML. Start here for web browsing, URL reading, and online documentation lookup.", + "inputSchema": { + "type": "object", + "properties": { + "timeout": { + "type": "string", + "description": "Request timeout in seconds (default 10, max 30)." + }, + "url": { + "type": "string", + "description": "The full URL to fetch (https only)." + } + }, + "required": [ + "url" + ] + } + }, + { + "name": "nomad_list_jobs", + "description": "List all jobs in the Nomad cluster. Start here for workload orchestration, container scheduling, and discovering running services.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression (e.g., Status == \"running\")" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + }, + "prefix": { + "type": "string", + "description": "Filter by job ID prefix" + } + } + } + }, + { + "name": "nomad_get_job", + "description": "Get full specification and status of a specific Nomad job, including task groups, constraints, and resource requirements. Use after list_jobs.", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "nomad_get_job_versions", + "description": "Get version history for a Nomad job. Shows previous configurations and when changes were made.", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "nomad_register_job", + "description": "Register (create or update) a Nomad job. Accepts a full job specification as JSON.", + "inputSchema": { + "type": "object", + "properties": { + "job": { + "type": "string", + "description": "Full job specification as JSON object" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + }, + "required": [ + "job" + ] + } + }, + { + "name": "nomad_stop_job", + "description": "Stop (deregister) a running Nomad job. All allocations will be stopped.", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + }, + "purge": { + "type": "string", + "description": "Completely purge the job from the system (true/false, default: false)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "nomad_force_evaluate", + "description": "Force a new evaluation for a Nomad job, triggering rescheduling. Useful when allocations are unhealthy or stuck.", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "nomad_list_allocations", + "description": "List all allocations across the Nomad cluster. Shows where tasks are placed and their health status.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + }, + "prefix": { + "type": "string", + "description": "Filter by allocation ID prefix" + } + } + } + }, + { + "name": "nomad_get_allocation", + "description": "Get details of a specific Nomad allocation, including task states, events, restart history, and resource usage.", + "inputSchema": { + "type": "object", + "properties": { + "alloc_id": { + "type": "string", + "description": "Allocation ID" + } + }, + "required": [ + "alloc_id" + ] + } + }, + { + "name": "nomad_get_job_allocations", + "description": "List all allocations for a specific Nomad job. Shows placement, health, and status of each task instance.", + "inputSchema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "description": "Job ID" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + }, + "required": [ + "job_id" + ] + } + }, + { + "name": "nomad_stop_allocation", + "description": "Stop a specific Nomad allocation. The scheduler may place a replacement depending on job configuration.", + "inputSchema": { + "type": "object", + "properties": { + "alloc_id": { + "type": "string", + "description": "Allocation ID" + } + }, + "required": [ + "alloc_id" + ] + } + }, + { + "name": "nomad_restart_allocation", + "description": "Restart a task within a Nomad allocation. Optionally specify which task to restart.", + "inputSchema": { + "type": "object", + "properties": { + "alloc_id": { + "type": "string", + "description": "Allocation ID" + }, + "task": { + "type": "string", + "description": "Task name (optional, restarts all tasks if omitted)" + } + }, + "required": [ + "alloc_id" + ] + } + }, + { + "name": "nomad_read_allocation_logs", + "description": "Read stdout or stderr logs from a task in a Nomad allocation. Use for debugging container and workload issues.", + "inputSchema": { + "type": "object", + "properties": { + "alloc_id": { + "type": "string", + "description": "Allocation ID" + }, + "log_type": { + "type": "string", + "description": "Log type: stdout or stderr (default: stdout)" + }, + "offset": { + "type": "string", + "description": "Byte offset to start reading from" + }, + "origin": { + "type": "string", + "description": "Log origin: start or end (default: end)" + }, + "plain": { + "type": "string", + "description": "Return plain text instead of JSON (true/false, default: true)" + }, + "task": { + "type": "string", + "description": "Task name" + } + }, + "required": [ + "alloc_id", + "task" + ] + } + }, + { + "name": "nomad_list_nodes", + "description": "List all client nodes in the Nomad cluster. Shows node status, datacenter, drivers, and scheduling eligibility.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression (e.g., Status == \"ready\")" + }, + "prefix": { + "type": "string", + "description": "Filter by node ID prefix" + } + } + } + }, + { + "name": "nomad_get_node", + "description": "Get full details of a specific Nomad node, including attributes, resources, drivers, host volumes, and metadata.", + "inputSchema": { + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Node ID" + } + }, + "required": [ + "node_id" + ] + } + }, + { + "name": "nomad_get_node_allocations", + "description": "List all allocations placed on a specific Nomad node. Shows what workloads are running on the node.", + "inputSchema": { + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Node ID" + } + }, + "required": [ + "node_id" + ] + } + }, + { + "name": "nomad_drain_node", + "description": "Enable or disable drain mode on a Nomad node. Draining migrates all allocations off the node for maintenance.", + "inputSchema": { + "type": "object", + "properties": { + "deadline": { + "type": "string", + "description": "Drain deadline duration (e.g., '1h', '30m'). Use -1 for no deadline" + }, + "enable": { + "type": "string", + "description": "Enable drain (true) or disable (false)" + }, + "ignore_system_jobs": { + "type": "string", + "description": "Skip draining system jobs (true/false, default: false)" + }, + "node_id": { + "type": "string", + "description": "Node ID" + } + }, + "required": [ + "enable", + "node_id" + ] + } + }, + { + "name": "nomad_node_eligibility", + "description": "Toggle scheduling eligibility for a Nomad node. Ineligible nodes won't receive new allocations but keep existing ones.", + "inputSchema": { + "type": "object", + "properties": { + "eligible": { + "type": "string", + "description": "Set eligible (true) or ineligible (false)" + }, + "node_id": { + "type": "string", + "description": "Node ID" + } + }, + "required": [ + "eligible", + "node_id" + ] + } + }, + { + "name": "nomad_list_deployments", + "description": "List deployments across the Nomad cluster. Shows rolling update status, canary progress, and deployment health.", + "inputSchema": { + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + }, + "prefix": { + "type": "string", + "description": "Filter by deployment ID prefix" + } + } + } + }, + { + "name": "nomad_get_deployment", + "description": "Get details of a specific Nomad deployment, including task group status, health, and canary information.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "nomad_promote_deployment", + "description": "Promote canary allocations in a Nomad deployment. Moves canaries to production after validation.", + "inputSchema": { + "type": "object", + "properties": { + "all": { + "type": "string", + "description": "Promote all task groups (true/false, default: true)" + }, + "deployment_id": { + "type": "string", + "description": "Deployment ID" + }, + "groups": { + "type": "string", + "description": "Comma-separated list of task groups to promote (alternative to all)" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "nomad_fail_deployment", + "description": "Mark a Nomad deployment as failed, triggering automatic rollback to the previous job version.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "nomad_list_evaluations", + "description": "List evaluations in the Nomad scheduler queue. Shows scheduling decisions, blocked evaluations, and failures.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Filter expression (e.g., Status == \"blocked\")" + }, + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + }, + "prefix": { + "type": "string", + "description": "Filter by evaluation ID prefix" + } + } + } + }, + { + "name": "nomad_list_services", + "description": "List all registered services in the Nomad service discovery catalog.", + "inputSchema": { + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "Namespace (default: default)" + } + } + } + }, + { + "name": "nomad_get_agent_self", + "description": "Get the current Nomad agent's configuration, stats, and node information. Useful for diagnosing agent state.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "nomad_get_cluster_status", + "description": "Get Nomad cluster status including Raft leader address and peer list. Use for cluster health checks.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "nomad_gc", + "description": "Trigger garbage collection on the Nomad cluster to clean up dead allocations, evaluations, and deployments.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "botidentity_gh_get_app", + "description": "Get the authenticated GitHub App's metadata including name, permissions, and events. Start here for GitHub bot and app identity management.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "botidentity_gh_list_installations", + "description": "List all installations of a GitHub App across organizations and users. Shows where the bot is installed and its access scope.", + "inputSchema": { + "type": "object", + "properties": { + "page": { + "type": "string", + "description": "Page number (default: 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default: 30, max: 100)" + } + } + } + }, + { + "name": "botidentity_gh_get_installation", + "description": "Get details of a specific GitHub App installation including target account, permissions, and repository access. Use after botidentity_gh_list_installations.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID (from botidentity_gh_list_installations)" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_create_install_token", + "description": "Create a short-lived installation access token for a GitHub App bot. Token grants the bot's permissions and expires in 1 hour. Use to authenticate bot API calls.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID to create token for" + }, + "permissions": { + "type": "string", + "description": "JSON object of permission overrides e.g. {\"contents\":\"read\",\"issues\":\"write\"} (optional, defaults to app permissions)" + }, + "repositories": { + "type": "string", + "description": "JSON array of repository names to scope the token to (optional, defaults to all)" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_list_install_repos", + "description": "List repositories accessible to a GitHub App installation. Shows which repos the bot identity can access.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID" + }, + "page": { + "type": "string", + "description": "Page number (default: 1)" + }, + "per_page": { + "type": "string", + "description": "Results per page (default: 30, max: 100)" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_add_install_repo", + "description": "Add a repository to a GitHub App installation. Grants the bot identity access to an additional repository.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID" + }, + "repository_id": { + "type": "string", + "description": "Repository ID to add (numeric ID, not name)" + } + }, + "required": [ + "installation_id", + "repository_id" + ] + } + }, + { + "name": "botidentity_gh_remove_install_repo", + "description": "Remove a repository from a GitHub App installation. Revokes the bot identity's access to a repository.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID" + }, + "repository_id": { + "type": "string", + "description": "Repository ID to remove (numeric ID, not name)" + } + }, + "required": [ + "installation_id", + "repository_id" + ] + } + }, + { + "name": "botidentity_gh_suspend_installation", + "description": "Suspend a GitHub App installation. Disables the bot identity without uninstalling. All API access and webhooks are paused.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID to suspend" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_unsuspend_installation", + "description": "Unsuspend a GitHub App installation. Re-enables a previously suspended bot identity, restoring API access and webhooks.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID to unsuspend" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_delete_installation", + "description": "Delete a GitHub App installation. Permanently removes the bot identity from an organization or user account. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "installation_id": { + "type": "string", + "description": "Installation ID to delete" + } + }, + "required": [ + "installation_id" + ] + } + }, + { + "name": "botidentity_gh_get_webhook_config", + "description": "Get the webhook configuration for a GitHub App. Shows delivery URL, content type, and SSL verification settings.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "botidentity_gh_update_webhook_config", + "description": "Update the webhook configuration for a GitHub App. Change the delivery URL, content type, secret, or SSL settings.", + "inputSchema": { + "type": "object", + "properties": { + "content_type": { + "type": "string", + "description": "Content type: 'json' or 'form' (default: 'form')" + }, + "insecure_ssl": { + "type": "string", + "description": "SSL verification: '0' (verify) or '1' (skip)" + }, + "secret": { + "type": "string", + "description": "Secret for HMAC signature verification" + }, + "url": { + "type": "string", + "description": "Webhook payload delivery URL" + } + } + } + }, + { + "name": "botidentity_gh_list_webhook_deliveries", + "description": "List recent webhook deliveries for a GitHub App. Shows delivery status, response codes, and timing for debugging bot event handling.", + "inputSchema": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "per_page": { + "type": "string", + "description": "Results per page (default: 30, max: 100)" + } + } + } + }, + { + "name": "botidentity_gh_redeliver_webhook", + "description": "Redeliver a failed webhook for a GitHub App. Retry a specific delivery that the bot failed to process.", + "inputSchema": { + "type": "object", + "properties": { + "delivery_id": { + "type": "string", + "description": "Webhook delivery ID to redeliver" + } + }, + "required": [ + "delivery_id" + ] + } + }, + { + "name": "botidentity_gh_create_app", + "description": "Create a GitHub App via the manifest flow. Starts a temporary local web server, opens a browser form that auto-submits to GitHub, waits for the user to approve, captures the redirect code, exchanges it for credentials, and stores everything in the bot inventory. All-in-one.", + "inputSchema": { + "type": "object", + "properties": { + "bot_id": { + "type": "string", + "description": "Bot inventory ID to store credentials on (from botidentity_create_bot)" + }, + "events": { + "type": "string", + "description": "JSON array of webhook events e.g. [\"push\",\"pull_request\"] (optional, sensible defaults provided)" + }, + "name": { + "type": "string", + "description": "GitHub App name" + }, + "org": { + "type": "string", + "description": "Organization slug to create the app under (optional, defaults to personal account)" + }, + "permissions": { + "type": "string", + "description": "JSON object of permissions e.g. {\"contents\":\"read\",\"issues\":\"write\"} (optional, sensible defaults provided)" + }, + "url": { + "type": "string", + "description": "App homepage URL (default: https://github.com)" + }, + "webhook_url": { + "type": "string", + "description": "Webhook delivery URL (optional)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "botidentity_slack_create_app", + "description": "Create a new Slack bot app from a manifest. Defines the bot's name, scopes, event subscriptions, slash commands, and OAuth settings. Start here for Slack bot identity creation.", + "inputSchema": { + "type": "object", + "properties": { + "manifest": { + "type": "string", + "description": "JSON string of the Slack app manifest defining bot name, scopes, features, and settings" + } + }, + "required": [ + "manifest" + ] + } + }, + { + "name": "botidentity_slack_update_app", + "description": "Update an existing Slack bot app's manifest. Modify the bot's name, scopes, event subscriptions, slash commands, or OAuth settings.", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "Slack app ID to update" + }, + "manifest": { + "type": "string", + "description": "JSON string of the full updated manifest (must include all fields, not just changes)" + } + }, + "required": [ + "app_id", + "manifest" + ] + } + }, + { + "name": "botidentity_slack_delete_app", + "description": "Permanently delete a Slack bot app. Removes the bot identity and revokes all tokens. Irreversible.", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "Slack app ID to delete" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "botidentity_slack_validate_app", + "description": "Validate a Slack bot app manifest without creating or updating. Check for errors before applying changes to bot configuration.", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "Optional existing app ID to validate against" + }, + "manifest": { + "type": "string", + "description": "JSON string of the Slack app manifest to validate" + } + }, + "required": [ + "manifest" + ] + } + }, + { + "name": "botidentity_slack_export_app", + "description": "Export the current manifest of an existing Slack bot app. Use to inspect a bot's configuration or as a template for creating new bots.", + "inputSchema": { + "type": "object", + "properties": { + "app_id": { + "type": "string", + "description": "Slack app ID to export manifest from" + } + }, + "required": [ + "app_id" + ] + } + }, + { + "name": "botidentity_slack_get_bot_info", + "description": "Get information about a Slack bot user including name, icons, and associated app. Use to verify bot identity details.", + "inputSchema": { + "type": "object", + "properties": { + "bot": { + "type": "string", + "description": "Bot user ID (e.g. B12345678)" + }, + "token": { + "type": "string", + "description": "Bot or user token with users:read scope (uses configured slack_config_token if omitted)" + } + }, + "required": [ + "bot" + ] + } + }, + { + "name": "botidentity_slack_rotate_token", + "description": "Rotate the Slack app configuration token using the refresh token. Returns a new access token and refresh token. The old tokens are invalidated. Config tokens expire after 12 hours.", + "inputSchema": { + "type": "object", + "properties": { + "refresh_token": { + "type": "string", + "description": "The xoxe refresh token (uses configured slack_refresh_token if omitted)" + } + } + } + }, + { + "name": "botidentity_slack_set_bot_icon", + "description": "Set a Slack bot's profile photo from a file path or base64-encoded image. Requires a bot token with users.profile:write scope. Use after botidentity_generate_logo to apply the generated image.", + "inputSchema": { + "type": "object", + "properties": { + "image_base64": { + "type": "string", + "description": "Base64-encoded PNG image data (alternative to path)" + }, + "path": { + "type": "string", + "description": "File path to a PNG image (e.g. from botidentity_generate_logo output). Preferred over image_base64." + }, + "token": { + "type": "string", + "description": "Bot token with users.profile:write scope (uses configured slack_bot_token if omitted)" + } + } + } + }, + { + "name": "botidentity_generate_logo", + "description": "Open an interactive logo generator in the browser. Preview logos, tweak the prompt, regenerate until satisfied, then confirm. Logo is compressed under 900KB and saved to ~/.config/switchboard/logos/\u003cbot-id\u003e when bot_id is provided. Start here for bot branding and avatar creation.", + "inputSchema": { + "type": "object", + "properties": { + "bot_id": { + "type": "string", + "description": "Bot inventory ID — saves logo to config dir and updates inventory automatically" + }, + "model_id": { + "type": "string", + "description": "Bedrock model ID (default: stability.stable-image-core-v1:1). Alternatives: stability.stable-image-ultra-v1:1, stability.sd3-5-large-v1:0" + }, + "negative_prompt": { + "type": "string", + "description": "Initial negative prompt (things to avoid)" + }, + "prompt": { + "type": "string", + "description": "Initial text prompt for logo generation" + } + } + } + }, + { + "name": "botidentity_create_bot", + "description": "Create a new bot identity across GitHub and Slack. Attempts to create on both platforms, registers in local inventory, and returns next steps for setup (install URLs, tokens to provide). Start here for new bot creation.", + "inputSchema": { + "type": "object", + "properties": { + "github": { + "type": "string", + "description": "Enable GitHub identity: 'true' (default) or 'false'" + }, + "id": { + "type": "string", + "description": "Unique bot identifier (e.g. 'deploy-bot', 'ci-notifier')" + }, + "name": { + "type": "string", + "description": "Human-readable display name for the bot" + }, + "slack": { + "type": "string", + "description": "Enable Slack identity: 'true' (default) or 'false'" + }, + "slack_manifest": { + "type": "string", + "description": "Custom Slack app manifest JSON (optional — a sensible default is generated from the bot name)" + } + }, + "required": [ + "id", + "name" + ] + } + }, + { + "name": "botidentity_delete_bot", + "description": "Delete a bot identity. Removes the Slack app (if created), provides instructions for GitHub App deletion, and removes the local inventory entry.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier to delete" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "botidentity_inv_list", + "description": "List all bots in the local inventory. Shows each bot's enabled platforms, app IDs, and logo. Filter by platform.", + "inputSchema": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "description": "Filter by platform: 'github' or 'slack' (optional, lists all if omitted)" + } + } + } + }, + { + "name": "botidentity_inv_get", + "description": "Get full details of a bot from the inventory including credentials (masked), platform status, and next steps for incomplete setup. Use after botidentity_inv_list.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "botidentity_inv_update", + "description": "Update a bot's profile in the inventory. Change name, logo, or platform-specific IDs.", + "inputSchema": { + "type": "object", + "properties": { + "github_app_id": { + "type": "string", + "description": "GitHub App ID (also enables GitHub if not already)" + }, + "github_app_slug": { + "type": "string", + "description": "GitHub App slug (for URL generation)" + }, + "id": { + "type": "string", + "description": "Bot identifier" + }, + "logo_path": { + "type": "string", + "description": "New logo image path" + }, + "name": { + "type": "string", + "description": "New display name" + }, + "slack_app_id": { + "type": "string", + "description": "Slack App ID (also enables Slack if not already)" + }, + "slack_bot_user_id": { + "type": "string", + "description": "Slack bot user ID" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "botidentity_inv_set_cred", + "description": "Store a credential for a bot. Well-known keys (slack_bot_token, github_private_key, github_webhook_secret, github_client_id, github_client_secret, slack_webhook_url) are stored in platform-specific fields. Other keys go to the generic credentials map.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + }, + "key": { + "type": "string", + "description": "Credential key (e.g. 'slack_bot_token', 'github_private_key', 'github_webhook_secret')" + }, + "value": { + "type": "string", + "description": "Credential value" + } + }, + "required": [ + "id", + "key", + "value" + ] + } + }, + { + "name": "botidentity_inv_delete_cred", + "description": "Remove a stored credential from a bot in the inventory.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + }, + "key": { + "type": "string", + "description": "Credential key to remove" + } + }, + "required": [ + "id", + "key" + ] + } + }, + { + "name": "botidentity_inv_get_creds", + "description": "List all credentials stored for a bot (values masked). Use botidentity_inv_get_cred to reveal a specific value.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "botidentity_inv_get_cred", + "description": "Get the full unmasked value of a specific credential for a bot. Use after botidentity_inv_get_creds to identify the key.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + }, + "key": { + "type": "string", + "description": "Credential key to retrieve" + } + }, + "required": [ + "id", + "key" + ] + } + }, + { + "name": "botidentity_inv_set_logo", + "description": "Set or update a bot's logo path in the inventory. Use after botidentity_generate_logo to associate the generated image.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + }, + "logo_path": { + "type": "string", + "description": "Path to logo image file" + } + }, + "required": [ + "id", + "logo_path" + ] + } + }, + { + "name": "botidentity_inv_set_meta", + "description": "Set a metadata key-value pair on a bot in the inventory. Use for descriptions, environment, team ownership, or any custom labels.", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Bot identifier" + }, + "key": { + "type": "string", + "description": "Metadata key (e.g. 'environment', 'team', 'description')" + }, + "value": { + "type": "string", + "description": "Metadata value" + } + }, + "required": [ + "id", + "key", + "value" + ] + } + }, + { + "name": "x_get_tweet", + "description": "Get a single tweet by ID. Returns full tweet object with text, author, metrics, and entities", + "inputSchema": { + "type": "object", + "properties": { + "expansions": { + "type": "string", + "description": "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys" + }, + "id": { + "type": "string", + "description": "Tweet ID" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text,conversation_id,reply_settings" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_tweets", + "description": "Get multiple tweets by IDs (up to 100). Start here for bulk tweet lookups", + "inputSchema": { + "type": "object", + "properties": { + "expansions": { + "type": "string", + "description": "Comma-separated expansions: author_id,referenced_tweets.id,attachments.media_keys" + }, + "ids": { + "type": "string", + "description": "Comma-separated tweet IDs (max 100)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields: attachments,author_id,created_at,entities,geo,id,lang,public_metrics,source,text" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields: id,name,username,profile_image_url,verified,public_metrics" + } + }, + "required": [ + "ids" + ] + } + }, + { + "name": "x_create_tweet", + "description": "Post a new tweet. Supports text, replies, quotes, and polls", + "inputSchema": { + "type": "object", + "properties": { + "poll_duration_minutes": { + "type": "string", + "description": "Poll duration in minutes (5-10080)" + }, + "poll_options": { + "type": "string", + "description": "Comma-separated poll options (2-4 choices)" + }, + "quote_tweet_id": { + "type": "string", + "description": "Tweet ID to quote" + }, + "reply_to": { + "type": "string", + "description": "Tweet ID to reply to" + }, + "text": { + "type": "string", + "description": "Tweet text (up to 280 chars)" + } + }, + "required": [ + "text" + ] + } + }, + { + "name": "x_delete_tweet", + "description": "Delete a tweet by ID. Must be authored by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tweet ID to delete" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_search_recent", + "description": "Search tweets from the last 7 days. Start here for most tweet discovery workflows. Supports the full X search query syntax", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "max_results": { + "type": "string", + "description": "Results per page (10-100, default 10)" + }, + "next_token": { + "type": "string", + "description": "Pagination token from previous response" + }, + "query": { + "type": "string", + "description": "Search query (required). Supports operators: from:, to:, is:retweet, has:media, has:links, lang:, -is:retweet, etc." + }, + "sort_order": { + "type": "string", + "description": "Order of results: recency or relevancy" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601: 2024-01-01T00:00:00Z)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields: author_id,created_at,public_metrics,source,text,conversation_id,entities" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "x_search_all", + "description": "Full-archive search across all tweets (requires Pro tier or higher). Use x_search_recent for 7-day window instead", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "max_results": { + "type": "string", + "description": "Results per page (10-500, default 10)" + }, + "next_token": { + "type": "string", + "description": "Pagination token from previous response" + }, + "query": { + "type": "string", + "description": "Search query (required). Same operators as x_search_recent" + }, + "sort_order": { + "type": "string", + "description": "Order of results: recency or relevancy" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "x_get_tweet_count", + "description": "Get count of tweets matching a search query from the last 7 days. Useful for analytics without retrieving full tweets", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "granularity": { + "type": "string", + "description": "Aggregation granularity: minute, hour, or day (default day)" + }, + "query": { + "type": "string", + "description": "Search query" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "x_get_quote_tweets", + "description": "Get tweets that quote a specific tweet", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tweet ID to get quotes for" + }, + "max_results": { + "type": "string", + "description": "Results per page (10-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_hide_reply", + "description": "Hide a reply to one of your tweets", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reply tweet ID to hide" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_unhide_reply", + "description": "Unhide a previously hidden reply", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reply tweet ID to unhide" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_user_tweets", + "description": "Get tweets posted by a specific user. Use user_id from x_get_user or x_get_user_by_username", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "exclude": { + "type": "string", + "description": "Comma-separated: retweets,replies" + }, + "max_results": { + "type": "string", + "description": "Results per page (5-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + }, + "user_id": { + "type": "string", + "description": "User ID (not username — use x_get_user_by_username to resolve)" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_get_user_mentions", + "description": "Get tweets mentioning a specific user", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "max_results": { + "type": "string", + "description": "Results per page (5-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_get_home_timeline", + "description": "Get the authenticated user's home timeline (reverse chronological). Requires user context auth", + "inputSchema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "description": "Newest UTC datetime (ISO 8601)" + }, + "exclude": { + "type": "string", + "description": "Comma-separated: retweets,replies" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "start_time": { + "type": "string", + "description": "Oldest UTC datetime (ISO 8601)" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + } + } + } + }, + { + "name": "x_get_user", + "description": "Get a user by their numeric ID. Use x_get_user_by_username if you have a @handle instead", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated: id,name,username,created_at,description,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_user_by_username", + "description": "Get a user by @username. Preferred entry point when you know the handle. Returns user ID needed for other endpoints", + "inputSchema": { + "type": "object", + "properties": { + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + }, + "username": { + "type": "string", + "description": "Username without @ prefix" + } + }, + "required": [ + "username" + ] + } + }, + { + "name": "x_get_users", + "description": "Get multiple users by IDs (up to 100)", + "inputSchema": { + "type": "object", + "properties": { + "ids": { + "type": "string", + "description": "Comma-separated user IDs (max 100)" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "ids" + ] + } + }, + { + "name": "x_search_users", + "description": "Search for users by name or username", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 10)" + }, + "query": { + "type": "string", + "description": "Search query" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "x_get_me", + "description": "Get the authenticated user's profile information", + "inputSchema": { + "type": "object", + "properties": { + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + } + } + }, + { + "name": "x_get_following", + "description": "Get users that a user follows", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-1000, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_get_followers", + "description": "Get users who follow a user", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-1000, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_follow_user", + "description": "Follow a user. Uses the authenticated user's ID automatically", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to follow" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_unfollow_user", + "description": "Unfollow a user", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to unfollow" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_get_blocked", + "description": "Get users blocked by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-1000, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + } + } + }, + { + "name": "x_block_user", + "description": "Block a user", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to block" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_unblock_user", + "description": "Unblock a user", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to unblock" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_get_muted", + "description": "Get users muted by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-1000, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + } + } + }, + { + "name": "x_mute_user", + "description": "Mute a user", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to mute" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_unmute_user", + "description": "Unmute a user", + "inputSchema": { + "type": "object", + "properties": { + "target_user_id": { + "type": "string", + "description": "User ID to unmute" + } + }, + "required": [ + "target_user_id" + ] + } + }, + { + "name": "x_get_liking_users", + "description": "Get users who liked a specific tweet", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_id": { + "type": "string", + "description": "Tweet ID" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_get_liked_tweets", + "description": "Get tweets liked by a specific user", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (10-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_like_tweet", + "description": "Like a tweet", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to like" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_unlike_tweet", + "description": "Unlike a previously liked tweet", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to unlike" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_get_retweeters", + "description": "Get users who retweeted a specific tweet", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_id": { + "type": "string", + "description": "Tweet ID" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_retweet", + "description": "Retweet a tweet", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to retweet" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_unretweet", + "description": "Remove a retweet", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to unretweet" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_get_bookmarks", + "description": "Get tweets bookmarked by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 10)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + } + } + } + }, + { + "name": "x_bookmark_tweet", + "description": "Bookmark a tweet", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to bookmark" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_remove_bookmark", + "description": "Remove a tweet from bookmarks", + "inputSchema": { + "type": "object", + "properties": { + "tweet_id": { + "type": "string", + "description": "Tweet ID to remove from bookmarks" + } + }, + "required": [ + "tweet_id" + ] + } + }, + { + "name": "x_get_list", + "description": "Get a list by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "list_fields": { + "type": "string", + "description": "Comma-separated: id,name,description,private,follower_count,member_count,owner_id,created_at" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_owned_lists", + "description": "Get lists owned by a user", + "inputSchema": { + "type": "object", + "properties": { + "list_fields": { + "type": "string", + "description": "Comma-separated list fields" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_id": { + "type": "string", + "description": "User ID" + } + }, + "required": [ + "user_id" + ] + } + }, + { + "name": "x_create_list", + "description": "Create a new list", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "List description" + }, + "name": { + "type": "string", + "description": "List name (1-25 chars)" + }, + "private": { + "type": "string", + "description": "Whether list is private (true/false, default false)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "x_update_list", + "description": "Update a list's name, description, or privacy setting", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "New list description" + }, + "id": { + "type": "string", + "description": "List ID" + }, + "name": { + "type": "string", + "description": "New list name" + }, + "private": { + "type": "string", + "description": "Whether list is private (true/false)" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_delete_list", + "description": "Delete a list. Must be owned by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID to delete" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_list_tweets", + "description": "Get tweets from a list's timeline", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "tweet_fields": { + "type": "string", + "description": "Comma-separated tweet fields" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_get_list_members", + "description": "Get members of a list", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_add_list_member", + "description": "Add a user to a list. Must own the list", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "user_id": { + "type": "string", + "description": "User ID to add" + } + }, + "required": [ + "id", + "user_id" + ] + } + }, + { + "name": "x_remove_list_member", + "description": "Remove a user from a list", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "user_id": { + "type": "string", + "description": "User ID to remove" + } + }, + "required": [ + "id", + "user_id" + ] + } + }, + { + "name": "x_get_list_followers", + "description": "Get users who follow a list", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "List ID" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "user_fields": { + "type": "string", + "description": "Comma-separated user fields" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_follow_list", + "description": "Follow a list", + "inputSchema": { + "type": "object", + "properties": { + "list_id": { + "type": "string", + "description": "List ID to follow" + } + }, + "required": [ + "list_id" + ] + } + }, + { + "name": "x_unfollow_list", + "description": "Unfollow a list", + "inputSchema": { + "type": "object", + "properties": { + "list_id": { + "type": "string", + "description": "List ID to unfollow" + } + }, + "required": [ + "list_id" + ] + } + }, + { + "name": "x_get_pinned_lists", + "description": "Get lists pinned by the authenticated user", + "inputSchema": { + "type": "object", + "properties": { + "list_fields": { + "type": "string", + "description": "Comma-separated list fields" + } + } + } + }, + { + "name": "x_pin_list", + "description": "Pin a list", + "inputSchema": { + "type": "object", + "properties": { + "list_id": { + "type": "string", + "description": "List ID to pin" + } + }, + "required": [ + "list_id" + ] + } + }, + { + "name": "x_unpin_list", + "description": "Unpin a list", + "inputSchema": { + "type": "object", + "properties": { + "list_id": { + "type": "string", + "description": "List ID to unpin" + } + }, + "required": [ + "list_id" + ] + } + }, + { + "name": "x_list_dm_events", + "description": "List recent DM events for the authenticated user. Rate limit: 15 requests per 15 minutes", + "inputSchema": { + "type": "object", + "properties": { + "dm_event_fields": { + "type": "string", + "description": "Comma-separated: id,text,event_type,dm_conversation_id,created_at,sender_id" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + } + } + } + }, + { + "name": "x_get_dm_conversation", + "description": "Get DM events with a specific user", + "inputSchema": { + "type": "object", + "properties": { + "dm_event_fields": { + "type": "string", + "description": "Comma-separated DM event fields" + }, + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 100)" + }, + "pagination_token": { + "type": "string", + "description": "Pagination token" + }, + "participant_id": { + "type": "string", + "description": "User ID of the other participant" + } + }, + "required": [ + "participant_id" + ] + } + }, + { + "name": "x_send_dm", + "description": "Send a direct message to a user in an existing conversation", + "inputSchema": { + "type": "object", + "properties": { + "participant_id": { + "type": "string", + "description": "User ID to send the DM to" + }, + "text": { + "type": "string", + "description": "Message text" + } + }, + "required": [ + "participant_id", + "text" + ] + } + }, + { + "name": "x_create_dm_conversation", + "description": "Create a new group DM conversation", + "inputSchema": { + "type": "object", + "properties": { + "participant_ids": { + "type": "string", + "description": "Comma-separated user IDs to include" + }, + "text": { + "type": "string", + "description": "Initial message text" + } + }, + "required": [ + "participant_ids", + "text" + ] + } + }, + { + "name": "x_get_space", + "description": "Get details about a X Space by ID", + "inputSchema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Space ID" + }, + "space_fields": { + "type": "string", + "description": "Comma-separated: id,title,state,host_ids,speaker_ids,participant_count,scheduled_start,created_at,lang" + } + }, + "required": [ + "id" + ] + } + }, + { + "name": "x_search_spaces", + "description": "Search for X Spaces by title", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Results per page (1-100, default 10)" + }, + "query": { + "type": "string", + "description": "Search query for Space titles" + }, + "space_fields": { + "type": "string", + "description": "Comma-separated space fields" + }, + "state": { + "type": "string", + "description": "Filter by state: live, scheduled, or all (default all)" + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "x_get_usage", + "description": "Get API usage statistics for tweet endpoints. Track your consumption", + "inputSchema": { + "type": "object", + "properties": { + "days": { + "type": "string", + "description": "Number of days to look back (1-90, default 7)" + } + } + } + }, + { + "name": "gcal_list_events", + "description": "List events on a calendar with filters for time range, search query, recurrence, and updates. Start here for browsing the schedule, finding meetings, or checking what's on the calendar.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary' for the user's primary calendar)" + }, + "ical_uid": { + "type": "string", + "description": "Filter to a specific iCalendar UID" + }, + "max_results": { + "type": "string", + "description": "Max results per page (default 250, max 2500)" + }, + "order_by": { + "type": "string", + "description": "Order: 'startTime' (requires single_events=true) or 'updated'" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "private_extended_property": { + "type": "string", + "description": "Repeated propertyName=value pairs, comma-separated, for private extended-property filters" + }, + "q": { + "type": "string", + "description": "Free-text search across summary, description, location, attendees" + }, + "shared_extended_property": { + "type": "string", + "description": "Repeated propertyName=value pairs, comma-separated, for shared extended-property filters" + }, + "show_deleted": { + "type": "string", + "description": "Include cancelled events (true/false)" + }, + "single_events": { + "type": "string", + "description": "Expand recurring events into individual instances (true/false, default false)" + }, + "time_max": { + "type": "string", + "description": "Upper bound (exclusive) RFC3339 timestamp" + }, + "time_min": { + "type": "string", + "description": "Lower bound (inclusive) RFC3339 timestamp, e.g. '2024-03-15T00:00:00Z'" + }, + "time_zone": { + "type": "string", + "description": "Time zone used in response (default: calendar's time zone)" + }, + "updated_min": { + "type": "string", + "description": "Only events updated at/after this RFC3339 timestamp" + } + } + } + }, + { + "name": "gcal_get_event", + "description": "Get a single calendar event by ID. Read the full meeting details including attendees, location, attachments, and conference data.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "event_id": { + "type": "string", + "description": "Event ID" + }, + "time_zone": { + "type": "string", + "description": "Time zone used in response (default: calendar's time zone)" + } + }, + "required": [ + "event_id" + ] + } + }, + { + "name": "gcal_create_event", + "description": "Create a calendar event. Schedule a meeting or appointment with attendees, time, location, and notifications.", + "inputSchema": { + "type": "object", + "properties": { + "attendees": { + "type": "string", + "description": "Comma-separated attendee emails" + }, + "body": { + "type": "string", + "description": "Raw JSON body — overrides all other fields. Use for advanced cases (attachments, custom reminders, extendedProperties)" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "color_id": { + "type": "string", + "description": "Color ID from gcal_get_colors event palette" + }, + "conference_data_version": { + "type": "string", + "description": "Set to '1' to enable conferenceData (e.g. auto-create Google Meet link via 'create_meet=true')" + }, + "create_meet": { + "type": "string", + "description": "If true, attaches a Google Meet conference (requires conference_data_version=1)" + }, + "description": { + "type": "string", + "description": "Event description (plain text or HTML)" + }, + "end": { + "type": "string", + "description": "End time RFC3339 OR YYYY-MM-DD for all-day (exclusive)" + }, + "location": { + "type": "string", + "description": "Event location" + }, + "recurrence": { + "type": "string", + "description": "Comma-separated RRULE/EXRULE/RDATE/EXDATE lines (e.g. 'RRULE:FREQ=WEEKLY;COUNT=10')" + }, + "reminders_use_default": { + "type": "string", + "description": "Use the calendar's default reminders (true/false)" + }, + "send_updates": { + "type": "string", + "description": "Send notifications: all, externalOnly, none (default none)" + }, + "start": { + "type": "string", + "description": "Start time RFC3339 (e.g. '2024-03-15T14:00:00-07:00') OR YYYY-MM-DD for all-day" + }, + "summary": { + "type": "string", + "description": "Event title" + }, + "time_zone": { + "type": "string", + "description": "Time zone for start/end (e.g. 'America/Los_Angeles')" + }, + "transparency": { + "type": "string", + "description": "Show as: opaque (busy) or transparent (free)" + }, + "visibility": { + "type": "string", + "description": "Visibility: default, public, private, confidential" + } + } + } + }, + { + "name": "gcal_update_event", + "description": "Replace a calendar event with PUT semantics — all fields must be provided. Prefer gcal_patch_event for partial updates.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Full JSON event resource (replaces the entire event)" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "event_id": { + "type": "string", + "description": "Event ID" + }, + "send_updates": { + "type": "string", + "description": "Send notifications: all, externalOnly, none (default none)" + } + }, + "required": [ + "body", + "event_id" + ] + } + }, + { + "name": "gcal_patch_event", + "description": "Partially update a calendar event. Edit a meeting's title, time, attendees, or other fields without replacing the entire event.", + "inputSchema": { + "type": "object", + "properties": { + "attendees": { + "type": "string", + "description": "Replace attendee list with comma-separated emails" + }, + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides other fields" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "color_id": { + "type": "string", + "description": "Color ID" + }, + "description": { + "type": "string", + "description": "New description (optional)" + }, + "end": { + "type": "string", + "description": "New end time RFC3339 or YYYY-MM-DD (optional)" + }, + "event_id": { + "type": "string", + "description": "Event ID" + }, + "location": { + "type": "string", + "description": "New location (optional)" + }, + "recurrence": { + "type": "string", + "description": "Replace recurrence rules (comma-separated lines)" + }, + "send_updates": { + "type": "string", + "description": "Send notifications: all, externalOnly, none (default none)" + }, + "start": { + "type": "string", + "description": "New start time RFC3339 or YYYY-MM-DD (optional)" + }, + "summary": { + "type": "string", + "description": "New title (optional)" + }, + "time_zone": { + "type": "string", + "description": "Time zone for start/end" + }, + "transparency": { + "type": "string", + "description": "Show as: opaque or transparent" + }, + "visibility": { + "type": "string", + "description": "Visibility: default, public, private, confidential" + } + }, + "required": [ + "event_id" + ] + } + }, + { + "name": "gcal_delete_event", + "description": "Delete a calendar event. Cancel a meeting; optionally notify attendees.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "event_id": { + "type": "string", + "description": "Event ID" + }, + "send_updates": { + "type": "string", + "description": "Send cancellation notifications: all, externalOnly, none (default none)" + } + }, + "required": [ + "event_id" + ] + } + }, + { + "name": "gcal_move_event", + "description": "Move an event to a different calendar (re-home a meeting from personal to shared, for example).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Source calendar ID (defaults to 'primary')" + }, + "destination": { + "type": "string", + "description": "Destination calendar ID" + }, + "event_id": { + "type": "string", + "description": "Event ID" + }, + "send_updates": { + "type": "string", + "description": "Send notifications: all, externalOnly, none (default none)" + } + }, + "required": [ + "destination", + "event_id" + ] + } + }, + { + "name": "gcal_list_instances", + "description": "List the individual instances of a recurring event. Expand a weekly meeting into the actual occurrences.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "event_id": { + "type": "string", + "description": "Recurring event ID" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "original_start": { + "type": "string", + "description": "Original start time of a specific instance (RFC3339)" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "show_deleted": { + "type": "string", + "description": "Include cancelled instances (true/false)" + }, + "time_max": { + "type": "string", + "description": "Upper bound RFC3339" + }, + "time_min": { + "type": "string", + "description": "Lower bound RFC3339" + }, + "time_zone": { + "type": "string", + "description": "Time zone used in response" + } + }, + "required": [ + "event_id" + ] + } + }, + { + "name": "gcal_quick_add_event", + "description": "Create an event from a natural-language string (e.g. 'Lunch with Sarah Friday 1pm'). Quick way to schedule when you have a free-form text description.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "send_updates": { + "type": "string", + "description": "Send notifications: all, externalOnly, none (default none)" + }, + "text": { + "type": "string", + "description": "Natural-language event description" + } + }, + "required": [ + "text" + ] + } + }, + { + "name": "gcal_import_event", + "description": "Import an event from an external calendar (preserves the iCalUID, organizer, and original creation time). Use for syncing events; use create_event for new events.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Full JSON event resource including iCalUID and organizer" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "conference_data_version": { + "type": "string", + "description": "Set to '1' to preserve conferenceData" + }, + "supports_attachments": { + "type": "string", + "description": "Whether the API client supports event attachments (true/false)" + } + }, + "required": [ + "body" + ] + } + }, + { + "name": "gcal_list_calendars", + "description": "List the calendars in the user's calendar list. Discover which calendars are available to query (primary, secondary, shared, subscribed).", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Max results per page (default 100, max 250)" + }, + "min_access_role": { + "type": "string", + "description": "Filter by minimum access role: freeBusyReader, reader, writer, owner" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "show_deleted": { + "type": "string", + "description": "Include deleted entries (true/false)" + }, + "show_hidden": { + "type": "string", + "description": "Include hidden entries (true/false)" + } + } + } + }, + { + "name": "gcal_get_calendar_subscription", + "description": "Get a single calendar-list entry (the user-specific view of a subscribed calendar: color, hidden state, default reminders).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_subscribe_calendar", + "description": "Subscribe to an existing calendar by inserting it into the user's calendar list.", + "inputSchema": { + "type": "object", + "properties": { + "background_color": { + "type": "string", + "description": "Background color hex (e.g. '#ffffff')" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID to subscribe to" + }, + "color_id": { + "type": "string", + "description": "Color ID from gcal_get_colors calendar palette" + }, + "color_rgb_format": { + "type": "string", + "description": "Set to 'true' to use the color_id/background_color/foreground_color values" + }, + "foreground_color": { + "type": "string", + "description": "Foreground color hex" + }, + "hidden": { + "type": "string", + "description": "Hide from UI (true/false)" + }, + "selected": { + "type": "string", + "description": "Show in calendar view (true/false)" + }, + "summary_override": { + "type": "string", + "description": "Custom display name for this user" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_update_calendar_subscription", + "description": "Update the user-specific properties of a calendar subscription (color, hidden state, summary override).", + "inputSchema": { + "type": "object", + "properties": { + "background_color": { + "type": "string", + "description": "Background color hex" + }, + "body": { + "type": "string", + "description": "Raw JSON body — overrides other fields" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID" + }, + "color_id": { + "type": "string", + "description": "Color ID" + }, + "color_rgb_format": { + "type": "string", + "description": "Set to 'true' to use the color hex values" + }, + "foreground_color": { + "type": "string", + "description": "Foreground color hex" + }, + "hidden": { + "type": "string", + "description": "Hide from UI (true/false)" + }, + "selected": { + "type": "string", + "description": "Show in calendar view (true/false)" + }, + "summary_override": { + "type": "string", + "description": "Custom display name for this user" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_unsubscribe_calendar", + "description": "Remove a calendar from the user's calendar list (unsubscribe — does not delete the underlying calendar).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_get_calendar", + "description": "Get the metadata of a calendar resource (summary, description, time zone, location).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + } + } + } + }, + { + "name": "gcal_create_calendar", + "description": "Create a new secondary calendar owned by the user.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON body — overrides other fields" + }, + "description": { + "type": "string", + "description": "Calendar description" + }, + "location": { + "type": "string", + "description": "Geographic location of the calendar" + }, + "summary": { + "type": "string", + "description": "Calendar title" + }, + "time_zone": { + "type": "string", + "description": "Time zone (e.g. 'America/Los_Angeles')" + } + }, + "required": [ + "summary" + ] + } + }, + { + "name": "gcal_update_calendar", + "description": "Update a calendar's metadata (summary, description, time zone).", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides other fields" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID" + }, + "description": { + "type": "string", + "description": "New description" + }, + "location": { + "type": "string", + "description": "New location" + }, + "summary": { + "type": "string", + "description": "New calendar title" + }, + "time_zone": { + "type": "string", + "description": "New time zone" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_delete_calendar", + "description": "Permanently delete a secondary calendar (cannot delete primary calendars; use gcal_clear_calendar for primary).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID" + } + }, + "required": [ + "calendar_id" + ] + } + }, + { + "name": "gcal_clear_calendar", + "description": "Clear all events from the user's primary calendar. Use only for the primary calendar; secondaries should be deleted instead.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + } + } + } + }, + { + "name": "gcal_list_acl", + "description": "List the access control rules (sharing) for a calendar.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "show_deleted": { + "type": "string", + "description": "Include deleted ACL entries (true/false)" + } + } + } + }, + { + "name": "gcal_get_acl", + "description": "Get a single access control rule by ID.", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "rule_id": { + "type": "string", + "description": "ACL rule ID" + } + }, + "required": [ + "rule_id" + ] + } + }, + { + "name": "gcal_create_acl", + "description": "Grant access to a calendar. Share with a specific user, group, or domain.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON body — overrides other fields" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "role": { + "type": "string", + "description": "Role: none, freeBusyReader, reader, writer, owner" + }, + "scope_type": { + "type": "string", + "description": "Scope type: default, user, group, domain" + }, + "scope_value": { + "type": "string", + "description": "Scope value (email for user/group, domain name for domain; omit for 'default')" + }, + "send_notifications": { + "type": "string", + "description": "Send notification email (true/false)" + } + }, + "required": [ + "role", + "scope_type" + ] + } + }, + { + "name": "gcal_update_acl", + "description": "Update a calendar access control rule (change the granted role).", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON body — overrides other fields" + }, + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "role": { + "type": "string", + "description": "New role: none, freeBusyReader, reader, writer, owner" + }, + "rule_id": { + "type": "string", + "description": "ACL rule ID" + }, + "scope_type": { + "type": "string", + "description": "Scope type (must match existing): default, user, group, domain" + }, + "scope_value": { + "type": "string", + "description": "Scope value" + } + }, + "required": [ + "rule_id" + ] + } + }, + { + "name": "gcal_delete_acl", + "description": "Revoke a calendar access control rule (unshare).", + "inputSchema": { + "type": "object", + "properties": { + "calendar_id": { + "type": "string", + "description": "Calendar ID (defaults to 'primary')" + }, + "rule_id": { + "type": "string", + "description": "ACL rule ID" + } + }, + "required": [ + "rule_id" + ] + } + }, + { + "name": "gcal_query_freebusy", + "description": "Check availability across calendars in a time range. Returns busy intervals — use to find open time slots for scheduling meetings.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON body — overrides other fields" + }, + "calendar_expansion_max": { + "type": "string", + "description": "Max number of calendars to expand from groups (default 50)" + }, + "group_expansion_max": { + "type": "string", + "description": "Max members per group expansion (default 100)" + }, + "items": { + "type": "string", + "description": "Comma-separated calendar IDs (or group IDs) to query" + }, + "time_max": { + "type": "string", + "description": "Upper bound RFC3339" + }, + "time_min": { + "type": "string", + "description": "Lower bound RFC3339" + }, + "time_zone": { + "type": "string", + "description": "Time zone for the response" + } + }, + "required": [ + "items", + "time_max", + "time_min" + ] + } + }, + { + "name": "gcal_list_settings", + "description": "List the user's Calendar settings (week start, timezone, default event length, working hours).", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + } + } + } + }, + { + "name": "gcal_get_setting", + "description": "Get a single user Calendar setting by ID.", + "inputSchema": { + "type": "object", + "properties": { + "setting_id": { + "type": "string", + "description": "Setting ID (e.g. 'timezone', 'weekStart', 'defaultEventLength')" + } + }, + "required": [ + "setting_id" + ] + } + }, + { + "name": "gcal_get_colors", + "description": "Get the color palettes available for calendars and events. Returns the mapping of color IDs to hex values.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "gchat_list_spaces", + "description": "List Google Chat spaces the authenticated user belongs to. Start here for chat, conversations, messages, group chats, rooms, direct messages, DMs, threads, channels — to discover the space IDs needed by every other gchat tool. Returns spaces (full resource names like 'spaces/AAQAtuk0o-A'), their type (SPACE / GROUP_CHAT / DIRECT_MESSAGE), and display name (for named rooms).", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional filter expression (e.g. 'space_type = \"SPACE\"' for named rooms only, 'space_type = \"DIRECT_MESSAGE\"' for DMs)" + }, + "page_size": { + "type": "string", + "description": "Optional max spaces per page (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + } + } + }, + { + "name": "gchat_get_space", + "description": "Retrieve a single Google Chat space by resource name. Returns the space's display name, type, threading state, and history state.", + "inputSchema": { + "type": "object", + "properties": { + "space_id": { + "type": "string", + "description": "The space ID — accepts either bare ID ('AAQAtuk0o-A') or full resource name ('spaces/AAQAtuk0o-A')" + } + }, + "required": [ + "space_id" + ] + } + }, + { + "name": "gchat_list_messages", + "description": "List messages in a Google Chat space (rooms, group chats, DMs). Returns messages with sender, text, createTime, lastUpdateTime, and thread info. Use filter to narrow by createTime range; use order_by='createTime desc' to get newest-first.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional filter expression (e.g. 'createTime \u003e \"2024-01-01T00:00:00Z\"', 'thread.name = \"spaces/X/threads/Y\"')" + }, + "order_by": { + "type": "string", + "description": "Optional ordering — 'createTime' (oldest first, default) or 'createTime desc' (newest first)" + }, + "page_size": { + "type": "string", + "description": "Optional max messages per page (1-1000, default 25)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "show_deleted": { + "type": "string", + "description": "Optional boolean — include deleted messages (default false)" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form)" + } + }, + "required": [ + "space_id" + ] + } + }, + { + "name": "gchat_get_message", + "description": "Retrieve a single Google Chat message by space + message ID. Returns the message text, sender, createTime, lastUpdateTime, thread, attachments, and any cardsV2 payload.", + "inputSchema": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "The message ID (the segment after 'messages/' in the message name — typically contains dots, e.g. 'UMOmwAAAAAE.UMOmwAAAAAE')" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form)" + } + }, + "required": [ + "message_id", + "space_id" + ] + } + }, + { + "name": "gchat_create_message", + "description": "Send a new message to a Google Chat space. Returns the created message including its resource name. Pass text for a plain-text message; pass cards_v2 (raw JSON) for a card message. Use thread_key with message_reply_option to reply in an existing thread.", + "inputSchema": { + "type": "object", + "properties": { + "cards_v2": { + "type": "string", + "description": "Optional raw JSON for cardsV2 payload (array of card objects per the Chat API spec). Use instead of or alongside text." + }, + "message_id": { + "type": "string", + "description": "Optional custom message ID (alphanumeric, max 56 chars, must start with 'client-')" + }, + "message_reply_option": { + "type": "string", + "description": "Optional reply behavior: 'REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD' (reply in thread; new thread if no match), 'REPLY_MESSAGE_OR_FAIL' (reply or fail)" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form) to send the message to" + }, + "text": { + "type": "string", + "description": "Message text (plain text; supports basic formatting like *bold*, _italic_, `code`, and \u003chttps://link|label\u003e annotations)" + }, + "thread_key": { + "type": "string", + "description": "Optional client-supplied thread key — messages sharing the same key go in one thread (requires message_reply_option)" + } + }, + "required": [ + "space_id" + ] + } + }, + { + "name": "gchat_update_message", + "description": "Update an existing Google Chat message (edit its text or replace its cardsV2 content). Uses PATCH semantics with an auto-generated updateMask — only the fields you pass are changed. Only messages sent by the authenticated user / app can be updated.", + "inputSchema": { + "type": "object", + "properties": { + "cards_v2": { + "type": "string", + "description": "Optional new cardsV2 payload (raw JSON array)" + }, + "message_id": { + "type": "string", + "description": "The message ID" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form)" + }, + "text": { + "type": "string", + "description": "Optional new message text" + } + }, + "required": [ + "message_id", + "space_id" + ] + } + }, + { + "name": "gchat_delete_message", + "description": "Delete a Google Chat message. Only messages sent by the authenticated user / app can be deleted. Set force=true to also delete any threaded replies when deleting the thread head.", + "inputSchema": { + "type": "object", + "properties": { + "force": { + "type": "string", + "description": "Optional boolean — when deleting the head of a thread, also delete the threaded replies (default false)" + }, + "message_id": { + "type": "string", + "description": "The message ID to delete" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form)" + } + }, + "required": [ + "message_id", + "space_id" + ] + } + }, + { + "name": "gchat_list_members", + "description": "List members (human users and chat apps) of a Google Chat space. Returns each member's name (resource id), role (ROLE_MEMBER / ROLE_MANAGER), state (JOINED / INVITED / NOT_A_MEMBER), and member type (HUMAN / BOT).", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional filter (e.g. 'member.type = \"HUMAN\"', 'role = \"ROLE_MANAGER\"')" + }, + "page_size": { + "type": "string", + "description": "Optional max members per page (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "show_invited": { + "type": "string", + "description": "Optional boolean — include invited members (default false; only JOINED members otherwise)" + }, + "space_id": { + "type": "string", + "description": "The space ID (bare or 'spaces/X' form)" + } + }, + "required": [ + "space_id" + ] + } + }, + { + "name": "gdocs_get_document", + "description": "Retrieve (get) a Google Docs document by ID, including its full structured content (paragraphs, tables, lists, headings, images, footnotes). Start here for reading document text, getting word counts, finding sections, or fetching raw structure for analysis. Returns the document body as markdown via the renderer.", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The Google Docs document ID (the long string in the document URL)" + }, + "include_tabs_content": { + "type": "string", + "description": "true/false — include content of all tabs (default false; the API returns the active tab only otherwise)" + }, + "suggestions_view_mode": { + "type": "string", + "description": "How suggested edits are surfaced: DEFAULT_FOR_CURRENT_ACCESS, SUGGESTIONS_INLINE, PREVIEW_SUGGESTIONS_ACCEPTED, PREVIEW_WITHOUT_SUGGESTIONS" + } + }, + "required": [ + "document_id" + ] + } + }, + { + "name": "gdocs_create_document", + "description": "Create a new, empty Google Docs document. Returns the new document including its ID, which you can then pass to gdocs_batch_update, gdocs_insert_text, or gdocs_append_text to add content. To save the document into a specific Drive folder, use gdrive_update_file afterward to set parents.", + "inputSchema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Document title (defaults to 'Untitled document')" + } + } + } + }, + { + "name": "gdocs_batch_update", + "description": "Apply a batch of edit requests to a Google Docs document — the full power of the Docs API. Use after gdocs_get_document to see existing indices. Each request is one of: insertText, deleteContentRange, replaceAllText, insertTableRow, insertPageBreak, updateTextStyle, updateParagraphStyle, createParagraphBullets, deleteParagraphBullets, insertInlineImage, and many more. Prefer the convenience tools (gdocs_insert_text, gdocs_append_text, gdocs_replace_text, gdocs_delete_content) for simple edits; use this for compound or formatting changes.", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The document ID to edit" + }, + "requests": { + "type": "string", + "description": "JSON array of Docs API Request objects (e.g. [{\"insertText\":{\"location\":{\"index\":1},\"text\":\"Hello\"}}])" + }, + "write_control_revision": { + "type": "string", + "description": "Optional required_revision_id — only apply if the document is at this revision" + }, + "write_control_target": { + "type": "string", + "description": "Optional target_revision_id — only apply if the document is at or below this revision (use revision_id from gdocs_get_document)" + } + }, + "required": [ + "document_id", + "requests" + ] + } + }, + { + "name": "gdocs_insert_text", + "description": "Insert plain text at a specific character index in a Google Docs document. Convenience wrapper over gdocs_batch_update for the most common edit. Use gdocs_get_document first to find the index; index 1 is the start of the document body (index 0 is reserved).", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The document ID" + }, + "index": { + "type": "string", + "description": "Insertion index (1-based; 1 = start of body). Use gdocs_append_text to insert at end." + }, + "text": { + "type": "string", + "description": "The text to insert (may contain newlines for paragraph breaks)" + } + }, + "required": [ + "document_id", + "index", + "text" + ] + } + }, + { + "name": "gdocs_append_text", + "description": "Append text to the end of a Google Docs document. Convenience wrapper that fetches the doc, computes the end index, and inserts. The simplest way to add content without needing to track indices.", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The document ID" + }, + "leading_newline": { + "type": "string", + "description": "true/false — prepend a newline before the appended text (default true for clean separation)" + }, + "text": { + "type": "string", + "description": "The text to append" + } + }, + "required": [ + "document_id", + "text" + ] + } + }, + { + "name": "gdocs_replace_text", + "description": "Find and replace all occurrences of a string in a Google Docs document. Convenience wrapper over the Docs API's replaceAllText request. Use match_case=true for case-sensitive replacement.", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The document ID" + }, + "find": { + "type": "string", + "description": "Text to find" + }, + "match_case": { + "type": "string", + "description": "true/false — case-sensitive matching (default false)" + }, + "replace": { + "type": "string", + "description": "Replacement text (use empty string to delete all matches)" + } + }, + "required": [ + "document_id", + "find", + "replace" + ] + } + }, + { + "name": "gdocs_delete_content", + "description": "Delete a range of content (text, images, tables) between two character indices in a Google Docs document. Convenience wrapper over the Docs API's deleteContentRange request. Use gdocs_get_document to find the indices first.", + "inputSchema": { + "type": "object", + "properties": { + "document_id": { + "type": "string", + "description": "The document ID" + }, + "end_index": { + "type": "string", + "description": "End of range to delete (exclusive)" + }, + "start_index": { + "type": "string", + "description": "Start of range to delete (inclusive)" + } + }, + "required": [ + "document_id", + "end_index", + "start_index" + ] + } + }, + { + "name": "gdrive_list_files", + "description": "Search and list files and folders in Drive. Start here for finding documents, photos, folders, or anything else by name, owner, or type. Uses Drive's expressive query syntax (e.g. \"name contains 'budget'\", \"mimeType='application/vnd.google-apps.folder'\").", + "inputSchema": { + "type": "object", + "properties": { + "corpora": { + "type": "string", + "description": "Bodies of items to query: user, drive, allDrives, domain" + }, + "drive_id": { + "type": "string", + "description": "ID of the shared drive to search (required when corpora=drive)" + }, + "fields": { + "type": "string", + "description": "Selector for response fields (e.g. 'nextPageToken,files(id,name,mimeType,parents)'). Defaults to a useful subset" + }, + "include_items_from_all_drives": { + "type": "string", + "description": "Include items from shared drives (true/false, requires supports_all_drives=true)" + }, + "order_by": { + "type": "string", + "description": "Comma-separated sort keys with optional 'desc' suffix: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime" + }, + "page_size": { + "type": "string", + "description": "Max results per page (default 100, max 1000)" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "q": { + "type": "string", + "description": "Drive search query (e.g. \"name contains 'report'\", \"'\u003cfolder_id\u003e' in parents\", \"mimeType='application/vnd.google-apps.document'\", \"trashed=false\"). See https://developers.google.com/drive/api/guides/search-files" + }, + "spaces": { + "type": "string", + "description": "Comma-separated spaces: drive, appDataFolder" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + } + } + }, + { + "name": "gdrive_get_file", + "description": "Get metadata for a single file or folder (name, mimeType, size, parents, modifiedTime, owners, permissions, etc.). Use this for everything except the file content itself — for content use gdrive_download_file or gdrive_export_file.", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Field selector (default '*' returns all). Example: 'id,name,mimeType,parents,size,modifiedTime'" + }, + "file_id": { + "type": "string", + "description": "File or folder ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_download_file", + "description": "Download the binary content of a non-Google file (PDFs, images, ZIPs, CSV, etc.). For Google Docs/Sheets/Slides use gdrive_export_file. Response is a JSON envelope with base64-encoded content plus content_type, or text content inline when content_type is text/*.", + "inputSchema": { + "type": "object", + "properties": { + "acknowledge_abuse": { + "type": "string", + "description": "Set to true to download a file flagged as malicious" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "max_bytes": { + "type": "string", + "description": "Cap downloaded bytes (default 5_000_000, hard max 10_000_000)" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_export_file", + "description": "Export a Google Docs Editors file (Docs, Sheets, Slides, Drawings) as a different MIME type. Common export types: 'application/pdf', 'text/plain', 'text/csv', 'text/markdown' (for Docs), 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' (.docx). Response is a JSON envelope with content_type plus either base64 content or inline text when content_type is text/*.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "Google Docs/Sheets/Slides/Drawings file ID" + }, + "max_bytes": { + "type": "string", + "description": "Cap exported bytes (default 5_000_000, hard max 10_000_000)" + }, + "mime_type": { + "type": "string", + "description": "Target MIME type to export as" + } + }, + "required": [ + "file_id", + "mime_type" + ] + } + }, + { + "name": "gdrive_create_file", + "description": "Create a new file or upload content. Two modes: (1) metadata-only — pass name/mime_type/parents; (2) with content — pass content (UTF-8 string) or content_base64 (binary). Use gdrive_create_folder for folders specifically.", + "inputSchema": { + "type": "object", + "properties": { + "app_properties": { + "type": "string", + "description": "JSON object string for appProperties" + }, + "body": { + "type": "string", + "description": "Raw JSON file metadata body — overrides convenience args except content/content_base64" + }, + "content": { + "type": "string", + "description": "Text content (mutually exclusive with content_base64)" + }, + "content_base64": { + "type": "string", + "description": "Base64-encoded binary content (mutually exclusive with content)" + }, + "description": { + "type": "string", + "description": "File description" + }, + "mime_type": { + "type": "string", + "description": "MIME type (e.g. 'text/plain', 'application/pdf', 'application/vnd.google-apps.document' to create a new Google Doc from plain text content)" + }, + "name": { + "type": "string", + "description": "File name" + }, + "parents": { + "type": "string", + "description": "Comma-separated parent folder IDs (omit for My Drive root)" + }, + "properties": { + "type": "string", + "description": "JSON object string for properties" + }, + "starred": { + "type": "string", + "description": "true/false" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gdrive_update_file", + "description": "Update an existing file's metadata and/or content. To rename, change parents, or update content. Use gdrive_trash_file for soft delete.", + "inputSchema": { + "type": "object", + "properties": { + "add_parents": { + "type": "string", + "description": "Comma-separated parent IDs to add" + }, + "body": { + "type": "string", + "description": "Raw JSON file metadata body — overrides convenience args" + }, + "content": { + "type": "string", + "description": "Text content to overwrite the file with" + }, + "content_base64": { + "type": "string", + "description": "Base64-encoded binary content to overwrite the file with" + }, + "description": { + "type": "string", + "description": "New description" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "mime_type": { + "type": "string", + "description": "New MIME type" + }, + "name": { + "type": "string", + "description": "New name" + }, + "remove_parents": { + "type": "string", + "description": "Comma-separated parent IDs to remove" + }, + "starred": { + "type": "string", + "description": "true/false" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + }, + "trashed": { + "type": "string", + "description": "true/false (prefer gdrive_trash_file)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_copy_file", + "description": "Copy a file to a new file (metadata + content). Note: cannot copy folders (use gdrive_list_files + repeated copies).", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON metadata body — overrides convenience args" + }, + "description": { + "type": "string", + "description": "New description" + }, + "file_id": { + "type": "string", + "description": "Source file ID" + }, + "name": { + "type": "string", + "description": "New file name (defaults to 'Copy of \u003coriginal\u003e')" + }, + "parents": { + "type": "string", + "description": "Comma-separated parent folder IDs" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_delete_file", + "description": "Permanently delete a file (no trash). Cannot be undone. For soft delete use gdrive_trash_file.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_trash_file", + "description": "Move a file to the trash (soft delete, reversible via gdrive_untrash_file).", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_untrash_file", + "description": "Restore a file from the trash.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_empty_trash", + "description": "Permanently delete all files in the user's trash. Cannot be undone.", + "inputSchema": { + "type": "object", + "properties": { + "drive_id": { + "type": "string", + "description": "Shared drive ID (omit to empty My Drive trash)" + } + } + } + }, + { + "name": "gdrive_create_folder", + "description": "Create a new folder. Convenience wrapper around gdrive_create_file with mime_type set to application/vnd.google-apps.folder.", + "inputSchema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Folder description" + }, + "name": { + "type": "string", + "description": "Folder name" + }, + "parents": { + "type": "string", + "description": "Comma-separated parent folder IDs (omit for My Drive root)" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gdrive_generate_ids", + "description": "Generate a set of file IDs that can be used in subsequent insert/copy requests. Useful for pre-allocating IDs before uploading.", + "inputSchema": { + "type": "object", + "properties": { + "count": { + "type": "string", + "description": "Number of IDs to generate (default 10, max 1000)" + }, + "space": { + "type": "string", + "description": "Space the IDs are for: drive (default) or appDataFolder" + }, + "type": { + "type": "string", + "description": "Resource type the IDs are for: files (default) or shortcuts" + } + } + } + }, + { + "name": "gdrive_list_permissions", + "description": "List the sharing permissions on a file or shared drive. Shows who has access at what role.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File or shared drive ID" + }, + "page_size": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + }, + "use_domain_admin_access": { + "type": "string", + "description": "true/false — issue as domain administrator" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_get_permission", + "description": "Get a specific permission entry by ID.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File or shared drive ID" + }, + "permission_id": { + "type": "string", + "description": "Permission ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id", + "permission_id" + ] + } + }, + { + "name": "gdrive_create_permission", + "description": "Share a file or folder. Grants a role to a user, group, domain, or anyone. By default sends an email notification.", + "inputSchema": { + "type": "object", + "properties": { + "allow_file_discovery": { + "type": "string", + "description": "true/false (for type=domain or anyone)" + }, + "body": { + "type": "string", + "description": "Raw JSON permission body — overrides convenience args" + }, + "domain": { + "type": "string", + "description": "Domain (required for type=domain)" + }, + "email_address": { + "type": "string", + "description": "Email (required for user/group)" + }, + "email_message": { + "type": "string", + "description": "Custom message to include in the notification email" + }, + "file_id": { + "type": "string", + "description": "File or shared drive ID" + }, + "move_to_new_owners_root": { + "type": "string", + "description": "true/false — when transferring ownership in My Drive" + }, + "role": { + "type": "string", + "description": "Role: owner, organizer, fileOrganizer, writer, commenter, reader" + }, + "send_notification_email": { + "type": "string", + "description": "true/false (default true for user/group)" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + }, + "transfer_ownership": { + "type": "string", + "description": "true/false — required when changing owner" + }, + "type": { + "type": "string", + "description": "Grantee type: user, group, domain, anyone" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_update_permission", + "description": "Update a permission's role.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides convenience args" + }, + "expiration_time": { + "type": "string", + "description": "RFC3339 expiration timestamp" + }, + "file_id": { + "type": "string", + "description": "File or shared drive ID" + }, + "permission_id": { + "type": "string", + "description": "Permission ID" + }, + "role": { + "type": "string", + "description": "New role: owner, organizer, fileOrganizer, writer, commenter, reader" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + }, + "transfer_ownership": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "file_id", + "permission_id" + ] + } + }, + { + "name": "gdrive_delete_permission", + "description": "Revoke a permission (unshare).", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File or shared drive ID" + }, + "permission_id": { + "type": "string", + "description": "Permission ID" + }, + "supports_all_drives": { + "type": "string", + "description": "true/false (default true)" + } + }, + "required": [ + "file_id", + "permission_id" + ] + } + }, + { + "name": "gdrive_list_revisions", + "description": "List a file's revision history.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "page_size": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_get_revision", + "description": "Get a specific revision's metadata.", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Field selector" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "revision_id": { + "type": "string", + "description": "Revision ID" + } + }, + "required": [ + "file_id", + "revision_id" + ] + } + }, + { + "name": "gdrive_update_revision", + "description": "Update a revision (e.g. keepForever to pin it from auto-cleanup).", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides convenience args" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "keep_forever": { + "type": "string", + "description": "true/false — pin from auto-cleanup" + }, + "publish_auto": { + "type": "string", + "description": "true/false — auto-publish subsequent revisions" + }, + "published": { + "type": "string", + "description": "true/false — publish revision" + }, + "published_outside_domain": { + "type": "string", + "description": "true/false" + }, + "revision_id": { + "type": "string", + "description": "Revision ID" + } + }, + "required": [ + "file_id", + "revision_id" + ] + } + }, + { + "name": "gdrive_delete_revision", + "description": "Permanently delete a file revision.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "revision_id": { + "type": "string", + "description": "Revision ID" + } + }, + "required": [ + "file_id", + "revision_id" + ] + } + }, + { + "name": "gdrive_list_comments", + "description": "List comments on a file.", + "inputSchema": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "File ID" + }, + "include_deleted": { + "type": "string", + "description": "true/false" + }, + "page_size": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "start_modified_time": { + "type": "string", + "description": "RFC3339 — only comments modified at/after this time" + } + }, + "required": [ + "file_id" + ] + } + }, + { + "name": "gdrive_get_comment", + "description": "Get a comment by ID, including its replies.", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "include_deleted": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "comment_id", + "file_id" + ] + } + }, + { + "name": "gdrive_create_comment", + "description": "Create a new comment on a file. Optionally anchored to a specific quote/range.", + "inputSchema": { + "type": "object", + "properties": { + "anchor": { + "type": "string", + "description": "Anchor JSON string — for anchored comments" + }, + "body": { + "type": "string", + "description": "Raw JSON comment body — overrides convenience args" + }, + "content": { + "type": "string", + "description": "Comment body (plain text or HTML)" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "quoted_file_content": { + "type": "string", + "description": "JSON object string with mimeType+value the comment quotes" + } + }, + "required": [ + "content", + "file_id" + ] + } + }, + { + "name": "gdrive_update_comment", + "description": "Update a comment's content or resolved status.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides convenience args" + }, + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "content": { + "type": "string", + "description": "New content" + }, + "file_id": { + "type": "string", + "description": "File ID" + }, + "resolved": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "comment_id", + "file_id" + ] + } + }, + { + "name": "gdrive_delete_comment", + "description": "Delete a comment.", + "inputSchema": { + "type": "object", + "properties": { + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "file_id": { + "type": "string", + "description": "File ID" + } + }, + "required": [ + "comment_id", + "file_id" + ] + } + }, + { + "name": "gdrive_create_reply", + "description": "Reply to a comment.", + "inputSchema": { + "type": "object", + "properties": { + "action": { + "type": "string", + "description": "Action: resolve or reopen (optional)" + }, + "body": { + "type": "string", + "description": "Raw JSON reply body — overrides convenience args" + }, + "comment_id": { + "type": "string", + "description": "Comment ID" + }, + "content": { + "type": "string", + "description": "Reply content" + }, + "file_id": { + "type": "string", + "description": "File ID" + } + }, + "required": [ + "comment_id", + "content", + "file_id" + ] + } + }, + { + "name": "gdrive_list_drives", + "description": "List the shared drives the user has access to.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Max results per page" + }, + "page_token": { + "type": "string", + "description": "Token for next page" + }, + "q": { + "type": "string", + "description": "Search query (e.g. \"name contains 'Engineering'\")" + }, + "use_domain_admin_access": { + "type": "string", + "description": "true/false — list all shared drives in the domain" + } + } + } + }, + { + "name": "gdrive_get_drive", + "description": "Get metadata for a single shared drive.", + "inputSchema": { + "type": "object", + "properties": { + "drive_id": { + "type": "string", + "description": "Shared drive ID" + }, + "use_domain_admin_access": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "drive_id" + ] + } + }, + { + "name": "gdrive_create_drive", + "description": "Create a new shared drive.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Shared drive name" + }, + "request_id": { + "type": "string", + "description": "Unique idempotency key (auto-generated if omitted)" + }, + "theme_id": { + "type": "string", + "description": "Theme ID — for the drive cover image" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gdrive_update_drive", + "description": "Update a shared drive's metadata (name, theme, restrictions).", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Raw JSON patch body — overrides convenience args (use for restrictions, hidden, etc.)" + }, + "drive_id": { + "type": "string", + "description": "Shared drive ID" + }, + "name": { + "type": "string", + "description": "New name" + }, + "theme_id": { + "type": "string", + "description": "New theme ID" + }, + "use_domain_admin_access": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "drive_id" + ] + } + }, + { + "name": "gdrive_delete_drive", + "description": "Delete a shared drive. The drive must be empty.", + "inputSchema": { + "type": "object", + "properties": { + "allow_item_deletion": { + "type": "string", + "description": "true/false — delete non-empty drive (domain admin only)" + }, + "drive_id": { + "type": "string", + "description": "Shared drive ID" + }, + "use_domain_admin_access": { + "type": "string", + "description": "true/false" + } + }, + "required": [ + "drive_id" + ] + } + }, + { + "name": "gdrive_hide_drive", + "description": "Hide a shared drive from the user's default view.", + "inputSchema": { + "type": "object", + "properties": { + "drive_id": { + "type": "string", + "description": "Shared drive ID" + } + }, + "required": [ + "drive_id" + ] + } + }, + { + "name": "gdrive_unhide_drive", + "description": "Restore a shared drive to the user's default view.", + "inputSchema": { + "type": "object", + "properties": { + "drive_id": { + "type": "string", + "description": "Shared drive ID" + } + }, + "required": [ + "drive_id" + ] + } + }, + { + "name": "gdrive_get_about", + "description": "Get information about the authenticated user and their Drive (storage quota, supported MIME types, etc.).", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Field selector (default '*'). Example: 'user,storageQuota'" + } + } + } + }, + { + "name": "gforms_get_form", + "description": "Retrieve (get) a Google Forms form by ID, including all questions (items), info (title/description), settings, and the linked responder URL. Start here for surveys, quizzes, polls, questionnaires, feedback forms — to discover question item IDs for batchUpdate, inspect existing form structure, or read the form's response-collection settings. Use gforms_list_responses to fetch submitted answers.", + "inputSchema": { + "type": "object", + "properties": { + "form_id": { + "type": "string", + "description": "The Google Forms form ID (the long string in the URL between /d/ and /edit)" + } + }, + "required": [ + "form_id" + ] + } + }, + { + "name": "gforms_create_form", + "description": "Create a new Google Forms form (survey, quiz, poll, questionnaire). Only the title can be set at creation time per the Forms API; add questions via gforms_batch_update afterward. Returns the new form including its formId and the linked responderUri (public URL for submitters).", + "inputSchema": { + "type": "object", + "properties": { + "document_title": { + "type": "string", + "description": "Optional document title (shown in Drive). Defaults to the form title." + }, + "title": { + "type": "string", + "description": "Form title (shown to respondents)" + } + }, + "required": [ + "title" + ] + } + }, + { + "name": "gforms_batch_update", + "description": "Apply a batch of edit requests to a Google Forms form — the primary mutation API. Use for adding/deleting questions (items), updating titles or descriptions, changing settings (quiz mode, response collection), and moving items. Pass 'requests' as a JSON array of Forms API Request objects (e.g. [{\"createItem\":{\"item\":{\"title\":\"Q1\",\"questionItem\":{\"question\":{\"textQuestion\":{}}}},\"location\":{\"index\":0}}}]). For pure read access, use gforms_get_form instead.", + "inputSchema": { + "type": "object", + "properties": { + "form_id": { + "type": "string", + "description": "The form ID" + }, + "include_form_in_response": { + "type": "string", + "description": "Optional boolean — if true, the response includes the updated form" + }, + "requests": { + "type": "string", + "description": "JSON array of Forms API Request objects" + }, + "write_control_revision": { + "type": "string", + "description": "Optional required revision ID for optimistic concurrency (will fail if the form has been edited since this revision)" + }, + "write_control_target_revision": { + "type": "string", + "description": "Optional target revision ID — overrides required_revision_id when present" + } + }, + "required": [ + "form_id", + "requests" + ] + } + }, + { + "name": "gforms_list_responses", + "description": "List submitted responses (answers, submissions) for a Google Forms form. Returns paginated FormResponse objects with respondent answers keyed by question item ID. Use to analyze survey results, quiz submissions, feedback, or any collected form data. Combine with gforms_get_form to map question IDs to question text.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional filter (e.g. 'timestamp \u003e 2024-01-01T00:00:00Z') — see Forms API filter syntax" + }, + "form_id": { + "type": "string", + "description": "The form ID" + }, + "page_size": { + "type": "string", + "description": "Optional max responses per page (1-5000, default 5000)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + }, + "required": [ + "form_id" + ] + } + }, + { + "name": "gforms_get_response", + "description": "Retrieve a single submitted response (answer, submission) from a Google Forms form by its response ID. Lighter than gforms_list_responses when you already have the responseId.", + "inputSchema": { + "type": "object", + "properties": { + "form_id": { + "type": "string", + "description": "The form ID" + }, + "response_id": { + "type": "string", + "description": "The response ID (from FormResponse.responseId)" + } + }, + "required": [ + "form_id", + "response_id" + ] + } + }, + { + "name": "gmeet_create_space", + "description": "Create a new Google Meet meeting space (meeting room). Start here for create meet, new meeting, schedule call, video call, conference room, meeting URL. Returns a Space with meetingUri (the join URL to share), meetingCode (the short code like 'abc-defg-hij'), and a stable resource name 'spaces/{id}'. The space is persistent — anyone with the join URL can start a conference at any time. Optionally pass 'config' to control who can join and moderation settings.", + "inputSchema": { + "type": "object", + "properties": { + "config": { + "type": "string", + "description": "Optional SpaceConfig (JSON object or string) controlling join behavior. Common fields: accessType ('OPEN' = anyone with link, 'TRUSTED' = signed-in only, 'RESTRICTED' = invited only — default OPEN), entryPointAccess ('ALL' or 'CREATOR_APP_ONLY'), moderation ('ON'/'OFF'), moderationRestrictions (object). Example: {\"accessType\":\"TRUSTED\",\"moderation\":\"ON\"}" + } + } + } + }, + { + "name": "gmeet_get_space", + "description": "Retrieve a Google Meet space by resource name or meeting code. Accepts the full 'spaces/{id}', a bare id, or a meeting code like 'abc-defg-hij'. Returns the Space including meetingUri, meetingCode, config, and (if a conference is currently in progress) activeConference.conferenceRecord — the conference record id you can use to look up live participants.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Space resource name ('spaces/{id}'), bare id, or meeting code ('abc-defg-hij'). Meeting codes are case-insensitive and hyphens are optional." + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gmeet_update_space", + "description": "Update a Meet space's configuration (PATCH). Only fields named in update_mask are replaced. Use to change accessType, moderation, moderationRestrictions, or entryPointAccess on an existing space. Requires the meetings.space.settings OAuth scope.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Space resource name ('spaces/{id}') or bare id" + }, + "space": { + "type": "string", + "description": "Space object (JSON object or string) with the new field values. Only the fields listed in update_mask are applied. Example: {\"config\":{\"accessType\":\"RESTRICTED\",\"moderation\":\"ON\"}}" + }, + "update_mask": { + "type": "string", + "description": "Required FieldMask of paths to update (e.g. 'config.accessType,config.moderation'). Valid: config.accessType, config.entryPointAccess, config.moderation, config.moderationRestrictions.chatRestriction, config.moderationRestrictions.reactionRestriction, config.moderationRestrictions.presentRestriction, config.moderationRestrictions.defaultJoinAsViewerType." + } + }, + "required": [ + "name", + "space", + "update_mask" + ] + } + }, + { + "name": "gmeet_end_active_conference", + "description": "End the conference currently in progress in a Meet space. All participants are disconnected. The space itself persists — anyone with the join link can start a new conference later. No-op (succeeds with empty body) if no conference is active.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Space resource name ('spaces/{id}') or bare id" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gmeet_list_conference_records", + "description": "List past Google Meet conferences (conference records) the user attended or owns. Use for queries like 'recent meetings', 'past calls', 'meetings I joined last week'. Returns each conference record with name, the space it was held in, startTime, and endTime. Supports an EBNF filter for date ranges, space, etc.", + "inputSchema": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional EBNF filter expression. Examples: 'space.meeting_code = \"abc-mnop-xyz\"', 'space.name = \"spaces/{spaceId}\"', 'start_time\u003e=\"2024-01-01T00:00:00Z\" AND end_time\u003c=\"2024-02-01T00:00:00Z\"'." + }, + "page_size": { + "type": "string", + "description": "Optional page size (1-50, default 10)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + } + } + }, + { + "name": "gmeet_get_conference_record", + "description": "Retrieve a single past conference (conference record) by name. Use when you already have a conference record id from a previous list call or from a space's activeConference.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Conference record resource name ('conferenceRecords/{id}') or bare id" + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "gmeet_list_participants", + "description": "List participants who attended a past conference. Returns each participant with their identity (signedinUser.displayName + user.email when available; anonymous attendees are reported separately), earliestStartTime, and latestEndTime. Useful for 'who was in the meeting' / 'attendee list' queries.", + "inputSchema": { + "type": "object", + "properties": { + "conference_record": { + "type": "string", + "description": "Conference record resource name ('conferenceRecords/{id}') or bare id" + }, + "filter": { + "type": "string", + "description": "Optional filter (e.g. 'earliest_start_time\u003e=\"2024-01-01T00:00:00Z\"')" + }, + "page_size": { + "type": "string", + "description": "Optional page size (1-100, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + }, + "required": [ + "conference_record" + ] + } + }, + { + "name": "gmeet_list_recordings", + "description": "List video recordings for a past conference. Each recording links to a Google Drive file (driveDestination.file) and has a state (FILE_GENERATED = ready to view, STARTED = still recording, ENDED = stopped). Most conferences have at most one recording.", + "inputSchema": { + "type": "object", + "properties": { + "conference_record": { + "type": "string", + "description": "Conference record resource name ('conferenceRecords/{id}') or bare id" + }, + "page_size": { + "type": "string", + "description": "Optional page size (1-10, default 10)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + }, + "required": [ + "conference_record" + ] + } + }, + { + "name": "gmeet_list_transcripts", + "description": "List captions transcripts for a past conference. Each transcript is delivered as a Google Doc (docsDestination.document). Use gmeet_list_transcript_entries to retrieve individual spoken segments from a transcript without downloading the Doc.", + "inputSchema": { + "type": "object", + "properties": { + "conference_record": { + "type": "string", + "description": "Conference record resource name ('conferenceRecords/{id}') or bare id" + }, + "page_size": { + "type": "string", + "description": "Optional page size (1-10, default 10)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + }, + "required": [ + "conference_record" + ] + } + }, + { + "name": "gmeet_list_transcript_entries", + "description": "List individual transcript entries (spoken segments) from a conference transcript. Each entry has the participant who spoke, the text they said, languageCode, startTime, and endTime — useful for searching what was said in a meeting without parsing the Doc. Order is chronological.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "transcript": { + "type": "string", + "description": "Transcript resource name ('conferenceRecords/{cid}/transcripts/{tid}'). Get it from gmeet_list_transcripts." + } + }, + "required": [ + "transcript" + ] + } + }, + { + "name": "gpeople_list_contacts", + "description": "List the authenticated user's Google Contacts (address book entries). Start here for contacts, people, address book, phone book, coworkers, emails of people the user knows — paginates through the full /people/me/connections collection. Returns each person with names, email addresses, phone numbers, organizations, and addresses by default; override person_fields for a different shape.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "person_fields": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). Valid values: addresses, ageRanges, biographies, birthdays, calendarUrls, clientData, coverPhotos, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, metadata, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, photos, relations, sipAddresses, skills, urls, userDefined." + }, + "sort_order": { + "type": "string", + "description": "Optional sort: LAST_MODIFIED_ASCENDING, LAST_MODIFIED_DESCENDING, FIRST_NAME_ASCENDING, or LAST_NAME_ASCENDING (default LAST_MODIFIED_DESCENDING)" + } + } + } + }, + { + "name": "gpeople_search_contacts", + "description": "Search the authenticated user's Google Contacts by name, email, phone, or organization. Use for queries like 'find John', 'who is jane@example.com', 'contacts at Acme'. Returns matched people with names, email addresses, phone numbers, organizations, and addresses by default. The search is across name + email + phone + nickname + organization; partial / prefix matches are supported.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-30, default 10). The People API caps searchContacts at 30 results per page." + }, + "query": { + "type": "string", + "description": "Substring to match against the contact's names/nicknames/emails/phone numbers/organizations (e.g. 'jane', 'acme.com')" + }, + "read_mask": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses'). See gpeople_list_contacts for valid values." + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "gpeople_get_person", + "description": "Retrieve a single person (contact or directory profile) by resource name. Accepts 'people/c12345', the bare ID 'c12345', or 'me' for the authenticated user's own profile. Use when you already have a resource name from a previous list/search call.", + "inputSchema": { + "type": "object", + "properties": { + "person_fields": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,addresses,biographies,urls,photos,metadata'). See gpeople_list_contacts for valid values." + }, + "resource_name": { + "type": "string", + "description": "Person resource name (e.g. 'people/c12345'), bare ID, or 'me' for the authenticated user" + } + }, + "required": [ + "resource_name" + ] + } + }, + { + "name": "gpeople_create_contact", + "description": "Create a new Google Contact in the authenticated user's address book. Pass a 'person' object containing the field arrays you want set (names, emailAddresses, phoneNumbers, organizations, addresses, biographies, urls, etc.). Returns the new person including its resourceName. Use person_fields to control which fields appear in the response.", + "inputSchema": { + "type": "object", + "properties": { + "person": { + "type": "string", + "description": "Person resource object (JSON object OR JSON string). Example: {\"names\":[{\"givenName\":\"Jane\",\"familyName\":\"Doe\"}],\"emailAddresses\":[{\"value\":\"jane@example.com\",\"type\":\"work\"}],\"phoneNumbers\":[{\"value\":\"+1-555-0100\",\"type\":\"mobile\"}],\"organizations\":[{\"name\":\"Acme\",\"title\":\"Engineer\"}]}" + }, + "person_fields": { + "type": "string", + "description": "Optional comma-separated field mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata')." + } + }, + "required": [ + "person" + ] + } + }, + { + "name": "gpeople_update_contact", + "description": "Update fields on an existing Google Contact. Uses PATCH semantics — only the fields named in update_person_fields are replaced. Requires the contact's etag (from a previous list/get) to detect concurrent modifications; mismatched etags return 400 with reason 'failedPrecondition'.", + "inputSchema": { + "type": "object", + "properties": { + "person": { + "type": "string", + "description": "Person object (JSON object OR JSON string) with the new field values and the current etag. The etag MUST be present and match the server's current value. Example: {\"etag\":\"%EgwBAi4...\",\"names\":[{\"givenName\":\"Jane\",\"familyName\":\"Smith\"}],\"emailAddresses\":[{\"value\":\"jane@new.com\",\"type\":\"work\"}]}" + }, + "person_fields": { + "type": "string", + "description": "Optional comma-separated mask controlling the response shape (default 'names,emailAddresses,phoneNumbers,organizations,addresses,metadata')." + }, + "resource_name": { + "type": "string", + "description": "Person resource name (e.g. 'people/c12345') or bare ID" + }, + "update_person_fields": { + "type": "string", + "description": "Required comma-separated mask of fields to update (e.g. 'names,emailAddresses'). Valid values: addresses, biographies, birthdays, calendarUrls, clientData, emailAddresses, events, externalIds, genders, imClients, interests, locales, locations, memberships, miscKeywords, names, nicknames, occupations, organizations, phoneNumbers, relations, sipAddresses, urls, userDefined. NOTE: photos and coverPhotos cannot be updated via this endpoint." + } + }, + "required": [ + "person", + "resource_name", + "update_person_fields" + ] + } + }, + { + "name": "gpeople_delete_contact", + "description": "Permanently delete a Google Contact. The contact is removed from the authenticated user's address book and is not recoverable. Returns no content on success.", + "inputSchema": { + "type": "object", + "properties": { + "resource_name": { + "type": "string", + "description": "Person resource name (e.g. 'people/c12345') or bare ID" + } + }, + "required": [ + "resource_name" + ] + } + }, + { + "name": "gpeople_list_directory_people", + "description": "List people from the authenticated user's Google Workspace directory (coworkers in the same organization). Requires the user to be on a Google Workspace account with directory access enabled. Returns each person with their name, email, phone, organization, and job title by default.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "read_mask": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata'). See gpeople_list_contacts for valid values." + }, + "sources": { + "type": "string", + "description": "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'). Values: DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT (shared contacts), DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE (organization members)." + } + } + } + }, + { + "name": "gpeople_search_directory_people", + "description": "Search the authenticated user's Google Workspace directory by name, email, or job title. Use for queries like 'find coworker John', 'who is the VP of Engineering', 'people on the design team'. Requires directory access on a Google Workspace account.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-500, default 30)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "query": { + "type": "string", + "description": "Substring to match against directory people's name/email/job title/department" + }, + "read_mask": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,organizations,locations,metadata')." + }, + "sources": { + "type": "string", + "description": "Optional comma-separated source types (default 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT,DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE')." + } + }, + "required": [ + "query" + ] + } + }, + { + "name": "gpeople_list_other_contacts", + "description": "List the authenticated user's 'Other contacts' — auto-collected contacts (people the user has emailed but never explicitly saved to their address book). Useful for surfacing implicit connections. Read-only.", + "inputSchema": { + "type": "object", + "properties": { + "page_size": { + "type": "string", + "description": "Optional page size (1-1000, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "read_mask": { + "type": "string", + "description": "Optional comma-separated field mask (default 'names,emailAddresses,phoneNumbers,metadata'). NOTE: otherContacts only supports a limited set of fields — names, emailAddresses, phoneNumbers, photos, metadata." + } + } + } + }, + { + "name": "gsheets_get_spreadsheet", + "description": "Retrieve (get) a Google Sheets spreadsheet by ID, including its sheet tabs, their properties (titles, grid sizes, hidden state, tab colors), and optional cell data. Start here when you need to discover what sheets/tabs exist, get sheetIds for batchUpdate, or fetch metadata. For raw cell values, use gsheets_get_values (faster and lighter). Set include_grid_data=true to embed cell data — only do this for small sheets, the response can be enormous.", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Optional partial-response field mask (e.g. 'sheets.properties,spreadsheetId') to trim the response" + }, + "include_grid_data": { + "type": "string", + "description": "true/false — embed full cell grid data in the response (default false; can be huge)" + }, + "ranges": { + "type": "string", + "description": "Optional comma-separated A1-notation ranges to limit the response to (e.g. 'Sheet1!A1:C10,Sheet2!A:B')" + }, + "spreadsheet_id": { + "type": "string", + "description": "The Google Sheets spreadsheet ID (the long string in the spreadsheet URL between /d/ and /edit)" + } + }, + "required": [ + "spreadsheet_id" + ] + } + }, + { + "name": "gsheets_create_spreadsheet", + "description": "Create a new Google Sheets spreadsheet. Returns the new spreadsheet including its ID, which you can pass to gsheets_update_values or gsheets_batch_update to add content. To save the spreadsheet into a specific Drive folder, use gdrive_update_file afterward to set parents.", + "inputSchema": { + "type": "object", + "properties": { + "sheet_titles": { + "type": "string", + "description": "Optional comma-separated list of initial sheet/tab titles (e.g. 'Summary,Data,Notes'). Default is one sheet named 'Sheet1'." + }, + "title": { + "type": "string", + "description": "Spreadsheet title (defaults to 'Untitled spreadsheet')" + } + } + } + }, + { + "name": "gsheets_get_values", + "description": "Read cell values from a Google Sheets range using A1 notation. The lightest-weight way to extract data — far smaller than gsheets_get_spreadsheet with grid data. Returns a 2-D array of cell values. Use 'Sheet1' or 'Sheet1!A1:D100'-style ranges. Empty trailing cells are omitted, so each row may have fewer columns than the header row.", + "inputSchema": { + "type": "object", + "properties": { + "date_time_render_option": { + "type": "string", + "description": "How dates/times are returned: SERIAL_NUMBER (default) or FORMATTED_STRING" + }, + "major_dimension": { + "type": "string", + "description": "ROWS (default) or COLUMNS — controls whether the outer array is rows or columns" + }, + "range": { + "type": "string", + "description": "A1-notation range (e.g. 'Sheet1!A1:D100', 'Data', 'Summary!A:C')" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + }, + "value_render_option": { + "type": "string", + "description": "How values are returned: FORMATTED_VALUE (default; what's shown in the UI), UNFORMATTED_VALUE (raw values), or FORMULA (cell formulas)" + } + }, + "required": [ + "range", + "spreadsheet_id" + ] + } + }, + { + "name": "gsheets_batch_get_values", + "description": "Read cell values from multiple A1 ranges in one Google Sheets request. Use when you need to pull values from several non-contiguous ranges (e.g. 'Summary!A1:B5' and 'Data!D1:D100') — much faster than multiple gsheets_get_values calls.", + "inputSchema": { + "type": "object", + "properties": { + "date_time_render_option": { + "type": "string", + "description": "SERIAL_NUMBER (default) or FORMATTED_STRING" + }, + "major_dimension": { + "type": "string", + "description": "ROWS (default) or COLUMNS" + }, + "ranges": { + "type": "string", + "description": "Comma-separated A1-notation ranges (e.g. 'Sheet1!A1:B5,Sheet2!C:C')" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + }, + "value_render_option": { + "type": "string", + "description": "FORMATTED_VALUE (default), UNFORMATTED_VALUE, or FORMULA" + } + }, + "required": [ + "ranges", + "spreadsheet_id" + ] + } + }, + { + "name": "gsheets_update_values", + "description": "Overwrite cell values in a Google Sheets range. The values array must match the range shape; cells in the range that aren't present in values are NOT cleared. Pass values as a JSON-encoded 2-D array (rows of columns). For inserting at the bottom of a table, prefer gsheets_append_values.", + "inputSchema": { + "type": "object", + "properties": { + "include_values_in_response": { + "type": "string", + "description": "true/false — return the updated values in the response (default false)" + }, + "major_dimension": { + "type": "string", + "description": "ROWS (default) or COLUMNS" + }, + "range": { + "type": "string", + "description": "A1-notation range to write to (e.g. 'Sheet1!A2:C2')" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + }, + "value_input_option": { + "type": "string", + "description": "USER_ENTERED (default — parse like a user typed: formulas evaluate, dates parse) or RAW (store the input verbatim)" + }, + "values": { + "type": "string", + "description": "JSON 2-D array of values (e.g. '[[\"a\",\"b\",\"c\"],[1,2,3]]') — strings, numbers, booleans, or null" + } + }, + "required": [ + "range", + "spreadsheet_id", + "values" + ] + } + }, + { + "name": "gsheets_append_values", + "description": "Append rows to the end of a Google Sheets table. Finds the first empty row at or below the given range and inserts there. Use this for log-style or transactional data where you want new rows added without overwriting. Returns the actual range that was updated.", + "inputSchema": { + "type": "object", + "properties": { + "include_values_in_response": { + "type": "string", + "description": "true/false — return the appended values (default false)" + }, + "insert_data_option": { + "type": "string", + "description": "OVERWRITE (default — write into existing cells if the table extends) or INSERT_ROWS (shift rows down)" + }, + "major_dimension": { + "type": "string", + "description": "ROWS (default) or COLUMNS" + }, + "range": { + "type": "string", + "description": "A1-notation range identifying the table (e.g. 'Sheet1' or 'Sheet1!A1') — Sheets searches downward from the top-left of this range for the next empty row." + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + }, + "value_input_option": { + "type": "string", + "description": "USER_ENTERED (default) or RAW" + }, + "values": { + "type": "string", + "description": "JSON 2-D array of values (e.g. '[[\"new\",\"row\"]]')" + } + }, + "required": [ + "range", + "spreadsheet_id", + "values" + ] + } + }, + { + "name": "gsheets_clear_values", + "description": "Clear the cell values in a Google Sheets range, leaving formatting and other properties intact. Use a full range like 'Sheet1!A:Z' to wipe all data on a tab. To also remove formatting/banding, use gsheets_batch_update with a 'updateCells' request and the relevant field mask.", + "inputSchema": { + "type": "object", + "properties": { + "range": { + "type": "string", + "description": "A1-notation range to clear (e.g. 'Sheet1!A2:Z')" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + } + }, + "required": [ + "range", + "spreadsheet_id" + ] + } + }, + { + "name": "gsheets_batch_update_values", + "description": "Update cell values for multiple A1 ranges in one Google Sheets request — a single atomic write covering many ranges. Pass 'data' as a JSON array of {range, values, major_dimension?} objects. Much faster than multiple gsheets_update_values calls.", + "inputSchema": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "JSON array of ValueRange objects (e.g. '[{\"range\":\"Sheet1!A1:B1\",\"values\":[[\"a\",\"b\"]]},{\"range\":\"Sheet2!A1\",\"values\":[[\"hello\"]]}]'). Each entry may include majorDimension." + }, + "include_values_in_response": { + "type": "string", + "description": "true/false — return updated values (default false)" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + }, + "value_input_option": { + "type": "string", + "description": "USER_ENTERED (default) or RAW" + } + }, + "required": [ + "data", + "spreadsheet_id" + ] + } + }, + { + "name": "gsheets_batch_update", + "description": "Apply a batch of structural edit requests to a Google Sheets spreadsheet — the full power of the Sheets API. Use for adding/deleting/renaming sheets, formatting cells, inserting columns, freezing panes, conditional formatting, charts, banding, protected ranges, named ranges, and so on. For pure value writes, use gsheets_update_values, gsheets_append_values, or gsheets_batch_update_values instead — those are simpler and don't need sheetIds.", + "inputSchema": { + "type": "object", + "properties": { + "include_spreadsheet_in_response": { + "type": "string", + "description": "true/false — include the full spreadsheet in the response (default false)" + }, + "requests": { + "type": "string", + "description": "JSON array of Sheets API Request objects (e.g. [{\"addSheet\":{\"properties\":{\"title\":\"NewTab\"}}}])" + }, + "response_include_grid_data": { + "type": "string", + "description": "true/false — include grid data if the spreadsheet is in the response (default false)" + }, + "response_ranges": { + "type": "string", + "description": "Optional comma-separated A1-notation ranges to limit the spreadsheet response to" + }, + "spreadsheet_id": { + "type": "string", + "description": "The spreadsheet ID" + } + }, + "required": [ + "requests", + "spreadsheet_id" + ] + } + }, + { + "name": "gslides_get_presentation", + "description": "Retrieve (get) a Google Slides presentation by ID, including its slides (pages), layouts, masters, and page elements (shapes, text, tables, images). Start here when you need to discover slide IDs for batchUpdate, inspect existing content, or read the textual content of a deck. The response can be very large for content-rich decks — use gslides_get_page for a single slide if you only need one. For thumbnails, use gslides_get_page_thumbnail.", + "inputSchema": { + "type": "object", + "properties": { + "fields": { + "type": "string", + "description": "Optional partial-response field mask (e.g. 'slides.objectId,title') to trim the response" + }, + "presentation_id": { + "type": "string", + "description": "The Google Slides presentation ID (the long string in the URL between /d/ and /edit)" + } + }, + "required": [ + "presentation_id" + ] + } + }, + { + "name": "gslides_create_presentation", + "description": "Create a new Google Slides presentation. Returns the new presentation including its ID and an initial blank slide. Pass the returned presentation_id to gslides_batch_update to add slides and content. To save the presentation into a specific Drive folder, use gdrive_update_file afterward to set parents.", + "inputSchema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Presentation title (defaults to 'Untitled presentation')" + } + } + } + }, + { + "name": "gslides_get_page", + "description": "Retrieve a single page (slide) from a Google Slides presentation by its object ID. Much lighter than gslides_get_presentation when you only need one slide's content or page elements. The page_object_id comes from the slides[].objectId field of a presentation response.", + "inputSchema": { + "type": "object", + "properties": { + "page_object_id": { + "type": "string", + "description": "The object ID of the slide page (from slides[].objectId)" + }, + "presentation_id": { + "type": "string", + "description": "The presentation ID" + } + }, + "required": [ + "page_object_id", + "presentation_id" + ] + } + }, + { + "name": "gslides_get_page_thumbnail", + "description": "Generate a thumbnail image URL for a single Google Slides page (slide). Returns a short-lived signed URL pointing to a PNG. Use thumbnail_size to control resolution (LARGE/MEDIUM/SMALL or unspecified). The mime_type defaults to PNG.", + "inputSchema": { + "type": "object", + "properties": { + "mime_type": { + "type": "string", + "description": "PNG (default) — currently the only supported value" + }, + "page_object_id": { + "type": "string", + "description": "The object ID of the slide page (from slides[].objectId)" + }, + "presentation_id": { + "type": "string", + "description": "The presentation ID" + }, + "thumbnail_size": { + "type": "string", + "description": "LARGE / MEDIUM / SMALL (default unspecified = medium)" + } + }, + "required": [ + "page_object_id", + "presentation_id" + ] + } + }, + { + "name": "gslides_batch_update", + "description": "Apply a batch of edit requests to a Google Slides presentation — the primary mutation API. Use for creating/deleting slides, inserting text, replacing text, formatting, inserting shapes/tables/images, moving page elements, applying themes, and so on. Pass 'requests' as a JSON array of Slides API Request objects (e.g. [{\"createSlide\":{}}, {\"insertText\":{\"objectId\":\"...\",\"text\":\"hello\"}}]). For pure read access, use gslides_get_presentation or gslides_get_page instead.", + "inputSchema": { + "type": "object", + "properties": { + "presentation_id": { + "type": "string", + "description": "The presentation ID" + }, + "requests": { + "type": "string", + "description": "JSON array of Slides API Request objects" + }, + "write_control_revision": { + "type": "string", + "description": "Optional required revision ID for optimistic concurrency control (will fail if the presentation has been edited since this revision)" + } + }, + "required": [ + "presentation_id", + "requests" + ] + } + }, + { + "name": "gtasks_list_tasklists", + "description": "List the authenticated user's Google Tasks tasklists (todo lists, task lists, todo categories). Start here for todos, to-do items, action items, daily tasks, reminders, checklists — to discover the tasklist IDs needed by every other gtasks tool. The default tasklist (named 'My Tasks' or similar) always exists for any Google account.", + "inputSchema": { + "type": "object", + "properties": { + "max_results": { + "type": "string", + "description": "Optional max tasklists per page (1-100, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + } + } + } + }, + { + "name": "gtasks_create_tasklist", + "description": "Create a new Google Tasks tasklist (todo list, task category). Returns the new tasklist including its id.", + "inputSchema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Tasklist title (shown in the Tasks UI)" + } + }, + "required": [ + "title" + ] + } + }, + { + "name": "gtasks_delete_tasklist", + "description": "Delete a Google Tasks tasklist. Permanently removes the list AND all tasks within it. Cannot delete the default tasklist.", + "inputSchema": { + "type": "object", + "properties": { + "tasklist_id": { + "type": "string", + "description": "The tasklist ID (from gtasks_list_tasklists)" + } + }, + "required": [ + "tasklist_id" + ] + } + }, + { + "name": "gtasks_list_tasks", + "description": "List tasks (todos, action items) within a Google Tasks tasklist. Returns tasks with their title, notes, due date, status (needsAction/completed), parent, and position. Use to view a user's todo list, find overdue items, or build a daily agenda alongside gcal_list_events. Combine with show_completed=true to include finished tasks.", + "inputSchema": { + "type": "object", + "properties": { + "completed_max": { + "type": "string", + "description": "Optional upper bound on completion time (RFC 3339 timestamp)" + }, + "completed_min": { + "type": "string", + "description": "Optional lower bound on completion time (RFC 3339 timestamp)" + }, + "due_max": { + "type": "string", + "description": "Optional upper bound on due date (RFC 3339 timestamp)" + }, + "due_min": { + "type": "string", + "description": "Optional lower bound on due date (RFC 3339 timestamp, e.g. 2024-01-01T00:00:00Z)" + }, + "max_results": { + "type": "string", + "description": "Optional max tasks per page (1-100, default 100)" + }, + "page_token": { + "type": "string", + "description": "Optional pagination token from a previous response's nextPageToken" + }, + "show_completed": { + "type": "string", + "description": "Optional boolean — include completed tasks (default false)" + }, + "show_deleted": { + "type": "string", + "description": "Optional boolean — include deleted tasks (default false)" + }, + "show_hidden": { + "type": "string", + "description": "Optional boolean — include hidden tasks (default false)" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID (from gtasks_list_tasklists)" + }, + "updated_min": { + "type": "string", + "description": "Optional lower bound on last-modified time (RFC 3339 timestamp)" + } + }, + "required": [ + "tasklist_id" + ] + } + }, + { + "name": "gtasks_get_task", + "description": "Retrieve a single Google Tasks task by ID. Useful when you already have the task ID from a previous list call.", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "The task ID (from gtasks_list_tasks)" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID" + } + }, + "required": [ + "task_id", + "tasklist_id" + ] + } + }, + { + "name": "gtasks_create_task", + "description": "Create a new task (todo, action item, reminder) in a Google Tasks tasklist. Returns the new task including its id. Use parent_id to make this a subtask of an existing task. Use previous_id to control position within siblings.", + "inputSchema": { + "type": "object", + "properties": { + "due": { + "type": "string", + "description": "Optional due date (RFC 3339 timestamp). Note: the Tasks API only stores the date portion; the time is always 00:00:00Z." + }, + "notes": { + "type": "string", + "description": "Optional task notes / description" + }, + "parent_id": { + "type": "string", + "description": "Optional parent task ID — makes this a subtask of an existing task" + }, + "previous_id": { + "type": "string", + "description": "Optional sibling task ID — new task will be positioned immediately after this one (omit to insert at top)" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID to add the task to" + }, + "title": { + "type": "string", + "description": "Task title" + } + }, + "required": [ + "tasklist_id", + "title" + ] + } + }, + { + "name": "gtasks_update_task", + "description": "Update fields on an existing Google Tasks task (mark complete, change title, edit notes, set due date). Uses PATCH semantics — only the fields you provide are changed. Set status='completed' to mark a task done; set status='needsAction' to reopen.", + "inputSchema": { + "type": "object", + "properties": { + "completed": { + "type": "string", + "description": "Optional completion timestamp (RFC 3339); usually set automatically when status='completed'" + }, + "deleted": { + "type": "string", + "description": "Optional boolean — soft-delete the task" + }, + "due": { + "type": "string", + "description": "Optional new due date (RFC 3339 timestamp)" + }, + "hidden": { + "type": "string", + "description": "Optional boolean — hide the task from the default view" + }, + "notes": { + "type": "string", + "description": "Optional new notes (pass empty string to clear)" + }, + "status": { + "type": "string", + "description": "Optional new status: 'needsAction' (open) or 'completed' (done)" + }, + "task_id": { + "type": "string", + "description": "The task ID" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID" + }, + "title": { + "type": "string", + "description": "Optional new title" + } + }, + "required": [ + "task_id", + "tasklist_id" + ] + } + }, + { + "name": "gtasks_delete_task", + "description": "Permanently delete a Google Tasks task. To soft-delete instead (so it can be recovered), use gtasks_update_task with deleted=true.", + "inputSchema": { + "type": "object", + "properties": { + "task_id": { + "type": "string", + "description": "The task ID" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID" + } + }, + "required": [ + "task_id", + "tasklist_id" + ] + } + }, + { + "name": "gtasks_move_task", + "description": "Move a Google Tasks task to a new position within its tasklist — reparent (make it a subtask) and/or reorder (place after a sibling). Returns the updated task.", + "inputSchema": { + "type": "object", + "properties": { + "parent_id": { + "type": "string", + "description": "Optional new parent task ID (omit for top-level)" + }, + "previous_id": { + "type": "string", + "description": "Optional sibling ID — task will be positioned immediately after this one (omit to move to top)" + }, + "task_id": { + "type": "string", + "description": "The task ID to move" + }, + "tasklist_id": { + "type": "string", + "description": "The tasklist ID" + } + }, + "required": [ + "task_id", + "tasklist_id" + ] + } + }, + { + "name": "gtasks_clear_completed", + "description": "Clear all completed tasks from a Google Tasks tasklist (hides them from the default view). Returns no content on success. Equivalent to the 'Delete all completed tasks' menu action in the Tasks UI — note: tasks are hidden, not permanently deleted.", + "inputSchema": { + "type": "object", + "properties": { + "tasklist_id": { + "type": "string", + "description": "The tasklist ID" + } + }, + "required": [ + "tasklist_id" + ] + } + }, + { + "name": "kubernetes_list_contexts", + "description": "List Kubernetes kubeconfig contexts, clusters, users, and namespaces. Start here for local Kubernetes context discovery before choosing a cluster.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "kubernetes_list_namespaces", + "description": "List Kubernetes namespaces in the cluster. Start here for Kubernetes cluster discovery and finding available environments.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "kubernetes_list_pods", + "description": "List Kubernetes pods with phase, node, labels, restarts, and readiness. Start here for workload health, container debugging, and finding failing pods.", + "inputSchema": { + "type": "object", + "properties": { + "all_namespaces": { + "type": "string", + "description": "Set true to list pods across all namespaces." + }, + "field_selector": { + "type": "string", + "description": "Kubernetes field selector, for example status.phase=Running." + }, + "label_selector": { + "type": "string", + "description": "Kubernetes label selector, for example app=api,tier=backend." + }, + "limit": { + "type": "string", + "description": "Maximum number of pods to return (default: 100, max: 500)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + } + } + }, + { + "name": "kubernetes_get_pod", + "description": "Get details for a specific Kubernetes pod, including containers, conditions, owner references, and recent status. Use after list_pods.", + "inputSchema": { + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + }, + "pod": { + "type": "string", + "description": "Pod name." + } + }, + "required": [ + "pod" + ] + } + }, + { + "name": "kubernetes_read_pod_logs", + "description": "Read logs from a Kubernetes pod container. Use after list_pods or get_pod for debugging crashes, errors, and workload failures.", + "inputSchema": { + "type": "object", + "properties": { + "container": { + "type": "string", + "description": "Container name (optional for single-container pods)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + }, + "pod": { + "type": "string", + "description": "Pod name." + }, + "previous": { + "type": "string", + "description": "Set true to read logs from the previous terminated container instance." + }, + "tail": { + "type": "string", + "description": "Number of log lines from the end (default: 200, max: 2000)." + }, + "timestamps": { + "type": "string", + "description": "Set true to include log timestamps." + } + }, + "required": [ + "pod" + ] + } + }, + { + "name": "kubernetes_list_events", + "description": "List Kubernetes events sorted by time. Use for debugging scheduling, image pulls, probes, restarts, and rollout failures.", + "inputSchema": { + "type": "object", + "properties": { + "all_namespaces": { + "type": "string", + "description": "Set true to list events across all namespaces." + }, + "field_selector": { + "type": "string", + "description": "Kubernetes field selector, for example involvedObject.name=api." + }, + "limit": { + "type": "string", + "description": "Maximum number of events to return (default: 100, max: 500)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + } + } + }, + { + "name": "kubernetes_list_deployments", + "description": "List Kubernetes deployments with replicas, availability, labels, and images. Use for workload discovery and rollout health checks.", + "inputSchema": { + "type": "object", + "properties": { + "all_namespaces": { + "type": "string", + "description": "Set true to list deployments across all namespaces." + }, + "label_selector": { + "type": "string", + "description": "Kubernetes label selector." + }, + "limit": { + "type": "string", + "description": "Maximum number of deployments to return (default: 100, max: 500)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + } + } + }, + { + "name": "kubernetes_get_deployment", + "description": "Get details for a specific Kubernetes deployment, including rollout conditions, replica counts, strategy, selectors, and images. Use after list_deployments.", + "inputSchema": { + "type": "object", + "properties": { + "deployment": { + "type": "string", + "description": "Deployment name." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + }, + "required": [ + "deployment" + ] + } + }, + { + "name": "kubernetes_rollout_status", + "description": "Check Kubernetes deployment rollout status and explain whether a rollout is complete, progressing, or stuck. Use after list_deployments or get_deployment.", + "inputSchema": { + "type": "object", + "properties": { + "deployment": { + "type": "string", + "description": "Deployment name." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + }, + "required": [ + "deployment" + ] + } + }, + { + "name": "kubernetes_list_services", + "description": "List Kubernetes services with type, cluster IPs, external IPs, ports, and selectors. Use for service discovery and network debugging.", + "inputSchema": { + "type": "object", + "properties": { + "all_namespaces": { + "type": "string", + "description": "Set true to list services across all namespaces." + }, + "label_selector": { + "type": "string", + "description": "Kubernetes label selector." + }, + "limit": { + "type": "string", + "description": "Maximum number of services to return (default: 100, max: 500)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + } + } + }, + { + "name": "kubernetes_list_ingresses", + "description": "List Kubernetes ingresses with hosts, paths, classes, TLS, and load balancer addresses. Use for HTTP routing and exposure debugging.", + "inputSchema": { + "type": "object", + "properties": { + "all_namespaces": { + "type": "string", + "description": "Set true to list ingresses across all namespaces." + }, + "label_selector": { + "type": "string", + "description": "Kubernetes label selector." + }, + "limit": { + "type": "string", + "description": "Maximum number of ingresses to return (default: 100, max: 500)." + }, + "namespace": { + "type": "string", + "description": "Namespace (omit to use configured namespace; use all_namespaces=true for all namespaces where supported)." + } + } + } + }, + { + "name": "kubernetes_list_nodes", + "description": "List Kubernetes cluster nodes with readiness, versions, roles, taints, capacity, and allocatable resources. Use for cluster capacity and node health debugging.", + "inputSchema": { + "type": "object", + "properties": { + "label_selector": { + "type": "string", + "description": "Kubernetes label selector." + }, + "limit": { + "type": "string", + "description": "Maximum number of nodes to return (default: 100, max: 500)." + } + } + } + }, + { + "name": "vercel_list_teams", + "description": "List Vercel teams and workspaces available to the token. Start here to discover team_id or team_slug for scoped project, deployment, domain, and environment workflows.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "since": { + "type": "string", + "description": "Lower timestamp cursor/filter in milliseconds" + }, + "until": { + "type": "string", + "description": "Upper timestamp cursor/filter in milliseconds" + } + } + } + }, + { + "name": "vercel_get_team", + "description": "Get details for a Vercel team or workspace. Use after vercel_list_teams.", + "inputSchema": { + "type": "object", + "properties": { + "team_id": { + "type": "string", + "description": "Team identifier (team_...)" + }, + "team_slug": { + "type": "string", + "description": "Team URL slug if team_id is not known" + } + } + } + }, + { + "name": "vercel_list_team_members", + "description": "List Vercel team members and roles. Use after vercel_list_teams to inspect workspace access.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "role": { + "type": "string", + "description": "Filter by team role" + }, + "search": { + "type": "string", + "description": "Search by name, username, or email" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + } + } + }, + { + "name": "vercel_list_user_events", + "description": "List Vercel audit and activity events such as deployments, login activity, and team changes.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "principal_id": { + "type": "string", + "description": "User or team principal ID" + }, + "project_ids": { + "type": "string", + "description": "Comma-separated project IDs" + }, + "since": { + "type": "string", + "description": "Lower timestamp cursor/filter in milliseconds" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "types": { + "type": "string", + "description": "Comma-separated event types" + }, + "until": { + "type": "string", + "description": "Upper timestamp cursor/filter in milliseconds" + }, + "with_payload": { + "type": "string", + "description": "Include event payload details (true/false)" + } + } + } + }, + { + "name": "vercel_list_projects", + "description": "List Vercel projects, apps, frontend sites, and repositories. Start here for deployment, environment variable, domain, and alias workflows.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "search": { + "type": "string", + "description": "Search projects by name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + } + } + }, + { + "name": "vercel_get_project", + "description": "Get Vercel project details including framework, repository linkage, targets, and configuration. Use after vercel_list_projects.", + "inputSchema": { + "type": "object", + "properties": { + "id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "id_or_name" + ] + } + }, + { + "name": "vercel_create_project", + "description": "Create a Vercel project. Provide body with project settings such as name, framework, gitRepository, buildCommand, outputDirectory, and installCommand.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Project create request object" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "body" + ] + } + }, + { + "name": "vercel_update_project", + "description": "Update Vercel project settings. Use after vercel_get_project to modify build, framework, command, or environment configuration.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Project update request object" + }, + "id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "body", + "id_or_name" + ] + } + }, + { + "name": "vercel_delete_project", + "description": "Delete a Vercel project. Use after vercel_list_projects or vercel_get_project when removing an app/site.", + "inputSchema": { + "type": "object", + "properties": { + "id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "id_or_name" + ] + } + }, + { + "name": "vercel_list_deployments", + "description": "List Vercel deployments, deploy history, build status, preview URLs, production rollouts, and failed deploys. Start here for CI/CD deployment debugging.", + "inputSchema": { + "type": "object", + "properties": { + "app": { + "type": "string", + "description": "Project/app name filter" + }, + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "project_id": { + "type": "string", + "description": "Project ID filter" + }, + "since": { + "type": "string", + "description": "Lower timestamp cursor/filter in milliseconds" + }, + "state": { + "type": "string", + "description": "Deployment state: BUILDING, ERROR, READY, CANCELED, QUEUED, INITIALIZING" + }, + "target": { + "type": "string", + "description": "Deployment target: production, preview, or staging" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "until": { + "type": "string", + "description": "Upper timestamp cursor/filter in milliseconds" + } + } + } + }, + { + "name": "vercel_get_deployment", + "description": "Get details for a specific Vercel deployment including state, URL, git source, target, and build metadata. Use after vercel_list_deployments.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID (dpl_...)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "vercel_create_deployment", + "description": "Create or redeploy a Vercel deployment. Provide body with name, files, gitMetadata, projectSettings, target, deploymentId, or withLatestCommit.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Deployment create request object" + }, + "force_new": { + "type": "string", + "description": "Force new deployment instead of reusing build cache (true/false)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "body" + ] + } + }, + { + "name": "vercel_cancel_deployment", + "description": "Cancel a queued or building Vercel deployment. Use after vercel_list_deployments when a build is stuck or no longer needed.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID (dpl_...)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "vercel_delete_deployment", + "description": "Delete a Vercel deployment. Use after vercel_list_deployments to remove old preview or failed deployments.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID (dpl_...)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "url": { + "type": "string", + "description": "Deployment URL alternative identifier" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "vercel_list_deployment_events", + "description": "List Vercel deployment build events and logs. Use after vercel_list_deployments or vercel_get_deployment to debug build errors and failed deploys.", + "inputSchema": { + "type": "object", + "properties": { + "builds": { + "type": "string", + "description": "Include build logs/events (true/false)" + }, + "deployment_id_or_url": { + "type": "string", + "description": "Deployment ID or URL" + }, + "direction": { + "type": "string", + "description": "Log direction: forward or backward" + }, + "limit": { + "type": "string", + "description": "Results per page (default 100)" + }, + "name": { + "type": "string", + "description": "Build step name filter" + }, + "since": { + "type": "string", + "description": "Lower timestamp cursor/filter in milliseconds" + }, + "status_code": { + "type": "string", + "description": "HTTP status code filter" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "until": { + "type": "string", + "description": "Upper timestamp cursor/filter in milliseconds" + } + }, + "required": [ + "deployment_id_or_url" + ] + } + }, + { + "name": "vercel_list_runtime_logs", + "description": "List Vercel runtime logs for a deployment. Use after vercel_get_deployment to inspect function, edge, and request logs.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID" + }, + "limit": { + "type": "string", + "description": "Results per page (default 100)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "project_id": { + "type": "string", + "description": "Project ID" + }, + "since": { + "type": "string", + "description": "Lower timestamp cursor/filter in milliseconds" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "until": { + "type": "string", + "description": "Upper timestamp cursor/filter in milliseconds" + } + }, + "required": [ + "deployment_id", + "project_id" + ] + } + }, + { + "name": "vercel_list_project_env_vars", + "description": "List Vercel project environment variables and secret metadata. Use after vercel_get_project to inspect production, preview, and development config.", + "inputSchema": { + "type": "object", + "properties": { + "git_branch": { + "type": "string", + "description": "Filter environment variables by git branch" + }, + "limit": { + "type": "string", + "description": "Results per page (default 20)" + }, + "next": { + "type": "string", + "description": "Pagination cursor from previous response" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "target": { + "type": "string", + "description": "Filter by target: production, preview, or development" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "project_id_or_name" + ] + } + }, + { + "name": "vercel_create_project_env_vars", + "description": "Create one or more Vercel project environment variables. Values are write-only; use after vercel_get_project.", + "inputSchema": { + "type": "object", + "properties": { + "envs": { + "type": "string", + "description": "Array of env var objects with key, value, target, type, gitBranch, comment" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + }, + "upsert": { + "type": "string", + "description": "Update existing variables with same key/target (true/false)" + } + }, + "required": [ + "envs", + "project_id_or_name" + ] + } + }, + { + "name": "vercel_update_project_env_var", + "description": "Update a Vercel project environment variable by ID. Use after vercel_list_project_env_vars.", + "inputSchema": { + "type": "object", + "properties": { + "body": { + "type": "string", + "description": "Environment variable update object" + }, + "env_id": { + "type": "string", + "description": "Environment variable ID" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "body", + "env_id", + "project_id_or_name" + ] + } + }, + { + "name": "vercel_delete_project_env_var", + "description": "Delete a Vercel project environment variable by ID. Use after vercel_list_project_env_vars.", + "inputSchema": { + "type": "object", + "properties": { + "env_id": { + "type": "string", + "description": "Environment variable ID" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "env_id", + "project_id_or_name" + ] + } + }, + { + "name": "vercel_add_project_domain", + "description": "Add a domain to a Vercel project. Use after vercel_get_project when configuring custom domains.", + "inputSchema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "description": "Domain name to add" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "domain", + "project_id_or_name" + ] + } + }, + { + "name": "vercel_remove_project_domain", + "description": "Remove a domain from a Vercel project. Use after vercel_get_project or vercel_get_domain_config.", + "inputSchema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "description": "Domain name to remove" + }, + "project_id_or_name": { + "type": "string", + "description": "Project ID or name" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "domain", + "project_id_or_name" + ] + } + }, + { + "name": "vercel_get_domain_config", + "description": "Get Vercel domain DNS configuration and misconfiguration status. Use when debugging custom domain setup.", + "inputSchema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "description": "Domain name" + }, + "project_id_or_name": { + "type": "string", + "description": "Optional project ID or name for stricter checks" + }, + "strict": { + "type": "string", + "description": "Perform strict configuration check (true/false)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "domain" + ] + } + }, + { + "name": "vercel_list_deployment_aliases", + "description": "List aliases assigned to a Vercel deployment. Use after vercel_get_deployment to inspect production and preview URLs.", + "inputSchema": { + "type": "object", + "properties": { + "deployment_id": { + "type": "string", + "description": "Deployment ID (dpl_...)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "deployment_id" + ] + } + }, + { + "name": "vercel_assign_deployment_alias", + "description": "Assign a custom alias URL to a Vercel deployment. Use after vercel_get_deployment for manual alias promotion.", + "inputSchema": { + "type": "object", + "properties": { + "alias": { + "type": "string", + "description": "Alias domain or URL to assign" + }, + "deployment_id": { + "type": "string", + "description": "Deployment ID (dpl_...)" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "alias", + "deployment_id" + ] + } + }, + { + "name": "vercel_delete_deployment_alias", + "description": "Delete a Vercel deployment alias by alias ID. Use after vercel_list_deployment_aliases.", + "inputSchema": { + "type": "object", + "properties": { + "alias_id": { + "type": "string", + "description": "Alias ID" + }, + "team_id": { + "type": "string", + "description": "Team identifier (defaults to configured team_id)" + }, + "team_slug": { + "type": "string", + "description": "Team slug (defaults to configured team_slug when team_id is empty)" + } + }, + "required": [ + "alias_id" + ] + } + }, + { + "name": "switchboard_list_integrations", + "description": "List all registered integrations with their enabled/healthy status, tool counts, and credential keys. Start here for Switchboard self-management, configuration, and integration setup. Shows which integrations are configured, which need credentials, and which are healthy.", + "inputSchema": { + "type": "object", + "properties": { + "enabled_only": { + "type": "string", + "description": "If true, only show enabled integrations (default: false)." + } + } + } + }, + { + "name": "switchboard_get_integration", + "description": "Get detailed information about a specific integration including credential keys (not values), tool list, enabled/healthy status, and configuration hints. Use after list_integrations to inspect a single integration before configuring it.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Integration name (e.g. \"github\", \"datadog\", \"slack\")." + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "switchboard_configure_integration", + "description": "Configure an integration by setting credentials and enabling or disabling it. Credentials are merged with existing values — send only the keys you want to update. Set enabled=false to disable an integration without removing its credentials.", + "inputSchema": { + "type": "object", + "properties": { + "credentials": { + "type": "string", + "description": "JSON object of credential key-value pairs to set (merged with existing)." + }, + "enabled": { + "type": "string", + "description": "Whether to enable the integration after configuring (default: true)." + }, + "name": { + "type": "string", + "description": "Integration name (e.g. \"github\", \"datadog\")." + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "switchboard_check_health", + "description": "Check connectivity health of one or all enabled integrations. Returns healthy/unhealthy status for each integration by calling its health endpoint. Use to verify credentials work and upstream APIs are reachable.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Optional: specific integration to check. Omit to check all enabled integrations." + } + } + } + }, + { + "name": "switchboard_browse_plugins", + "description": "Browse available plugins from configured marketplace manifest sources. Shows plugin name, description, latest version, and whether it is already installed. Use before install_plugin to discover available extensions.", + "inputSchema": { + "type": "object" + } + }, + { + "name": "switchboard_install_plugin", + "description": "Install a plugin from the marketplace by name or from a direct URL. Downloads the WASM module, verifies its checksum, and registers it. Requires a server restart to load the plugin. Use after browse_plugins.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Plugin name from the marketplace (mutually exclusive with url)." + }, + "url": { + "type": "string", + "description": "Direct URL to a .wasm file to install (mutually exclusive with name)." + } + } + } + }, + { + "name": "switchboard_uninstall_plugin", + "description": "Uninstall a marketplace plugin by name. Removes the WASM file and deregisters it from config. Requires a server restart to take effect.", + "inputSchema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the installed plugin to remove." + } + }, + "required": [ + "name" + ] + } + }, + { + "name": "switchboard_server_info", + "description": "Get Switchboard server information including version, enabled integration count, total tool count, marketplace status, and runtime metrics summary. Use for diagnostics, status checks, and understanding the current server state.", + "inputSchema": { + "type": "object" + } + } + ] +} \ No newline at end of file diff --git a/server/wire_test.go b/server/wire_test.go new file mode 100644 index 00000000..ea81c0a0 --- /dev/null +++ b/server/wire_test.go @@ -0,0 +1,225 @@ +package server + +import ( + "encoding/json" + "flag" + "os" + "sort" + "testing" + + mcp "github.com/daltoniam/switchboard" + "github.com/daltoniam/switchboard/integrations/acp" + "github.com/daltoniam/switchboard/integrations/agents" + "github.com/daltoniam/switchboard/integrations/amazon" + awsInt "github.com/daltoniam/switchboard/integrations/aws" + "github.com/daltoniam/switchboard/integrations/botidentity" + "github.com/daltoniam/switchboard/integrations/clickhouse" + "github.com/daltoniam/switchboard/integrations/cloudflare" + "github.com/daltoniam/switchboard/integrations/confluence" + "github.com/daltoniam/switchboard/integrations/datadog" + "github.com/daltoniam/switchboard/integrations/digitalocean" + "github.com/daltoniam/switchboard/integrations/elasticsearch" + flyInt "github.com/daltoniam/switchboard/integrations/fly" + "github.com/daltoniam/switchboard/integrations/gcal" + "github.com/daltoniam/switchboard/integrations/gchat" + gcpInt "github.com/daltoniam/switchboard/integrations/gcp" + "github.com/daltoniam/switchboard/integrations/gdocs" + "github.com/daltoniam/switchboard/integrations/gdrive" + "github.com/daltoniam/switchboard/integrations/gforms" + "github.com/daltoniam/switchboard/integrations/github" + "github.com/daltoniam/switchboard/integrations/gmail" + "github.com/daltoniam/switchboard/integrations/gmeet" + "github.com/daltoniam/switchboard/integrations/gpeople" + "github.com/daltoniam/switchboard/integrations/gsheets" + "github.com/daltoniam/switchboard/integrations/gslides" + "github.com/daltoniam/switchboard/integrations/gtasks" + "github.com/daltoniam/switchboard/integrations/jira" + "github.com/daltoniam/switchboard/integrations/kubernetes" + "github.com/daltoniam/switchboard/integrations/linear" + "github.com/daltoniam/switchboard/integrations/metabase" + nomadInt "github.com/daltoniam/switchboard/integrations/nomad" + notionInt "github.com/daltoniam/switchboard/integrations/notion" + "github.com/daltoniam/switchboard/integrations/ollama" + "github.com/daltoniam/switchboard/integrations/pganalyze" + "github.com/daltoniam/switchboard/integrations/postgres" + "github.com/daltoniam/switchboard/integrations/posthog" + "github.com/daltoniam/switchboard/integrations/rwx" + "github.com/daltoniam/switchboard/integrations/salesforce" + "github.com/daltoniam/switchboard/integrations/sentry" + signozInt "github.com/daltoniam/switchboard/integrations/signoz" + slackInt "github.com/daltoniam/switchboard/integrations/slack" + snowflakeInt "github.com/daltoniam/switchboard/integrations/snowflake" + "github.com/daltoniam/switchboard/integrations/stripe" + "github.com/daltoniam/switchboard/integrations/suno" + switchboardInt "github.com/daltoniam/switchboard/integrations/switchboard" + "github.com/daltoniam/switchboard/integrations/vercel" + webfetchInt "github.com/daltoniam/switchboard/integrations/webfetch" + xInt "github.com/daltoniam/switchboard/integrations/x" + "github.com/daltoniam/switchboard/integrations/ynab" + "github.com/daltoniam/switchboard/registry" + "github.com/stretchr/testify/require" +) + +// updateLock regenerates tools_list.lock.json instead of asserting +// against it. Run after an intentional change to a tool's name, description, +// parameters, or required flags: +// +// go test ./server -run TestToolsList_MatchesWireLock -update +// +// then commit the updated lock alongside the YAML change. +var updateLock = flag.Bool("update", false, "regenerate the tools/list wire lock instead of checking it") + +// wireLockPath is the locked snapshot of the full tools/list output the server +// hands to the model. It is a lock file in the same sense as go.sum: a generated +// artifact derived from the tools.yaml source of truth, committed to the repo so +// CI can detect drift. It sits beside the server package (not under testdata) +// because it locks the server's own wire output. You do not hand-edit it; you +// regenerate it with -update. +const wireLockPath = "tools_list.lock.json" + +// TestToolsList_MatchesWireLock asserts the live tools/list wire output matches +// the committed lock byte-for-byte. It catches any change — accidental or +// intentional — to what the model sees: a dropped description, a renamed +// parameter, a flipped required flag. An intentional change is expected to fail +// here; rerun with -update to regenerate the lock and commit it with the change. +func TestToolsList_MatchesWireLock(t *testing.T) { + got := captureToolsListJSON(t) + + if *updateLock { + require.NoError(t, os.WriteFile(wireLockPath, got, 0o644)) + t.Logf("regenerated %s", wireLockPath) + return + } + + want, err := os.ReadFile(wireLockPath) + require.NoError(t, err) + + require.JSONEq(t, string(want), string(got), + "tools/list output drifted from %s. If this change is intentional, "+ + "regenerate the lock: go test ./server -run TestToolsList_MatchesWireLock -update", + wireLockPath) +} + +// captureToolsListJSON projects all registered adapter tools into the MCP wire +// shape and returns the JSON. Required arrays are sorted alphabetically — JSON +// Schema treats required as a set semantically, so the sort is a normalization +// that makes the wire output deterministic regardless of how the source types +// order their required entries. +func captureToolsListJSON(t *testing.T) []byte { + t.Helper() + + srv := buildAllIntegrationsServer(t) + + type wireProp struct { + Type string `json:"type"` + Description string `json:"description"` + } + type wireSchema struct { + Type string `json:"type"` + Properties map[string]wireProp `json:"properties,omitempty"` + Required []string `json:"required,omitempty"` + } + type wireTool struct { + Name string `json:"name"` + Description string `json:"description"` + InputSchema wireSchema `json:"inputSchema"` + } + + tools := make([]wireTool, 0, len(srv.allTools)) + for _, twi := range srv.allTools { + td := twi.Tool + + props := make(map[string]wireProp, len(td.Parameters)) + var required []string + for _, p := range td.Parameters { + props[string(p.Name)] = wireProp{Type: "string", Description: p.Description} + if p.Required { + required = append(required, string(p.Name)) + } + } + sort.Strings(required) + + schema := wireSchema{Type: "object", Properties: props} + if len(required) > 0 { + schema.Required = required + } + + tools = append(tools, wireTool{ + Name: string(td.Name), + Description: td.Description, + InputSchema: schema, + }) + } + + data, err := json.MarshalIndent(map[string]any{"tools": tools}, "", " ") + require.NoError(t, err) + return data +} + +// buildAllIntegrationsServer registers every production integration and returns +// a Server with discoverAll so the search index covers everything. +func buildAllIntegrationsServer(t *testing.T) *Server { + t.Helper() + + reg := registry.New() + for _, i := range []mcp.Integration{ + github.New(), + datadog.New(), + linear.New("https://mcp.linear.app"), + sentry.New(), + slackInt.New(), + metabase.New(), + awsInt.New(), + posthog.New(), + postgres.New(), + clickhouse.New(), + elasticsearch.New(), + pganalyze.New(), + rwx.New(), + ynab.New(), + stripe.New(), + amazon.New(), + gmail.New(), + jira.New(), + confluence.New(), + notionInt.New(), + ollama.New(), + gcpInt.New(), + suno.New(), + salesforce.New(), + cloudflare.New(), + digitalocean.New(), + flyInt.New(), + snowflakeInt.New(), + acp.New(), + agents.New(), + signozInt.New(), + webfetchInt.New(), + nomadInt.New(), + botidentity.New(), + xInt.New(), + gcal.New(), + gchat.New(), + gdocs.New(), + gdrive.New(), + gforms.New(), + gmeet.New(), + gpeople.New(), + gsheets.New(), + gslides.New(), + gtasks.New(), + kubernetes.New(), + vercel.New(), + } { + require.NoError(t, reg.Register(i), "registering %s", i.Name()) + } + + services := &mcp.Services{ + Config: newMockConfigService(nil), + Registry: reg, + } + switchboardIntegration := switchboardInt.New(services) + require.NoError(t, reg.Register(switchboardIntegration)) + + return New(services, WithDiscoverAll(true)) +} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..3de153fb --- /dev/null +++ b/tools.go @@ -0,0 +1,207 @@ +package mcp + +import ( + "bytes" + "errors" + "fmt" + "io" + + "gopkg.in/yaml.v3" +) + +// toolsDoc is the top-level envelope. Tools is kept as a raw yaml.Node so we +// can walk its Content (key/value alternating pairs) manually — this preserves +// declaration order and enables duplicate-key detection, neither of which is +// possible when decoding into a map[string]T. +type toolsDoc struct { + Version int `yaml:"version"` + Tools yaml.Node `yaml:"tools"` +} + +// LoadToolsYAML parses an embedded tools YAML document into []ToolDefinition. +// +// Schema: +// +// version: 1 +// tools: +// : +// description: "" # required +// parameters: # optional; omit or use ~ for no parameters +// : +// description: "" +// required: true # only `true` is permitted; absence means optional +// +// Strict-key behaviour: unknown keys at every level (top-level, tool entry, +// parameter entry) are rejected with an error naming the offending key. This +// catches typos like `descripton:` or `requird: true` at load time. +// +// Declaration order: tools and parameters are returned in the order they +// appear in the YAML document. Map-based decoding would randomise this order; +// the yaml.Node walk here preserves it. +// +// required semantics: absence of `required:` is equivalent to optional. +// Only `required: true` is meaningful; explicitly writing `required: false` +// is rejected as a parse error (absence already conveys optional). +// +// MustLoadToolsYAML wraps this function and panics on error. It is intended +// for use with embedded FS data at program init — not safe for user-supplied +// input. +func LoadToolsYAML(data []byte) ([]ToolDefinition, error) { + if len(data) == 0 { + return nil, errors.New("tools.yaml: empty input") + } + + var raw toolsDoc + dec := yaml.NewDecoder(bytes.NewReader(data)) + dec.KnownFields(true) + if err := dec.Decode(&raw); err != nil { + return nil, fmt.Errorf("tools.yaml: parse: %w", err) + } + + // Guard against multi-document YAML: a second Decode must return io.EOF. + // Use `any` as the sentinel type so a well-formed second document parses + // cleanly (err == nil) — with a struct{} target plus KnownFields(true) any + // content errors, masking the multi-doc case behind a parse error. + var rest any + switch err := dec.Decode(&rest); { + case err == nil: + return nil, errors.New("tools.yaml: multi-document yaml not supported") + case !errors.Is(err, io.EOF): + return nil, fmt.Errorf("tools.yaml: trailing content after first document: %w", err) + } + + if raw.Version != 1 { + return nil, fmt.Errorf("tools.yaml: unsupported version %d (want 1)", raw.Version) + } + + // tools: missing → Kind==0; tools: ~ → ScalarNode with tag !!null; tools: {} → empty MappingNode. + toolsNode := raw.Tools + isNull := toolsNode.Kind == yaml.ScalarNode && toolsNode.Tag == "!!null" + isEmpty := toolsNode.Kind == yaml.MappingNode && len(toolsNode.Content) == 0 + if toolsNode.Kind == 0 || isNull || isEmpty { + return nil, errors.New("tools.yaml: tools must be a non-empty mapping") + } + if toolsNode.Kind != yaml.MappingNode { + return nil, errors.New("tools.yaml: tools must be a mapping") + } + + seen := make(map[string]struct{}, len(toolsNode.Content)/2) + out := make([]ToolDefinition, 0, len(toolsNode.Content)/2) + + for i := 0; i < len(toolsNode.Content); i += 2 { + nameNode := toolsNode.Content[i] + entryNode := toolsNode.Content[i+1] + toolName := nameNode.Value + + if _, dup := seen[toolName]; dup { + return nil, fmt.Errorf("tools.yaml: duplicate tool name %q", toolName) + } + seen[toolName] = struct{}{} + + desc, params, err := walkToolEntryNode(entryNode) + if err != nil { + return nil, fmt.Errorf("tools.yaml: tool %q: %w", toolName, err) + } + out = append(out, ToolDefinition{ + Name: ToolName(toolName), + Description: desc, + Parameters: params, + }) + } + return out, nil +} + +// walkToolEntryNode walks a tool's value node (a mapping with keys +// "description" and optionally "parameters"). Any other key is an error. +func walkToolEntryNode(node *yaml.Node) (desc string, params []Parameter, err error) { + if node.Kind != yaml.MappingNode { + return "", nil, errors.New("expected mapping") + } + var paramsNode *yaml.Node + for i := 0; i < len(node.Content); i += 2 { + key := node.Content[i].Value + val := node.Content[i+1] + switch key { + case "description": + desc = val.Value + case "parameters": + paramsNode = val + default: + return "", nil, fmt.Errorf("unknown key %q", key) + } + } + if desc == "" { + return "", nil, errors.New("description must be non-empty") + } + if paramsNode == nil { + return desc, nil, nil + } + params, err = walkParameterNode(paramsNode) + if err != nil { + return "", nil, err + } + return desc, params, nil +} + +// walkParameterNode iterates a yaml.MappingNode and preserves declaration +// order. yaml.v3 populates MappingNode.Content as key, value, key, value, ... +// A missing key (Kind == 0) or null scalar (`parameters: ~`) is accepted and +// returns nil, nil. +func walkParameterNode(node *yaml.Node) ([]Parameter, error) { + if node.Kind == 0 || (node.Kind == yaml.ScalarNode && node.Tag == "!!null") { + return nil, nil + } + if node.Kind != yaml.MappingNode { + return nil, errors.New("parameters: expected mapping") + } + out := make([]Parameter, 0, len(node.Content)/2) + for i := 0; i < len(node.Content); i += 2 { + keyNode := node.Content[i] + valNode := node.Content[i+1] + + var pe struct { + description string + required bool + } + // Walk the parameter's value node strictly — no unknown keys permitted. + if valNode.Kind != yaml.MappingNode { + return nil, fmt.Errorf("parameter %q: expected mapping", keyNode.Value) + } + for j := 0; j < len(valNode.Content); j += 2 { + k := valNode.Content[j].Value + v := valNode.Content[j+1] + switch k { + case "description": + pe.description = v.Value + case "required": + if err := v.Decode(&pe.required); err != nil { + return nil, fmt.Errorf("parameter %q: required: %w", keyNode.Value, err) + } + if !pe.required { + return nil, fmt.Errorf("parameter %q: required: false is not allowed (omit the key — absence already conveys optional)", keyNode.Value) + } + default: + return nil, fmt.Errorf("parameter %q: unknown key %q", keyNode.Value, k) + } + } + if pe.description == "" { + return nil, fmt.Errorf("parameter %q: description must be non-empty", keyNode.Value) + } + out = append(out, Parameter{ + Name: ParamName(keyNode.Value), + Description: pe.description, + Required: pe.required, + }) + } + return out, nil +} + +// MustLoadToolsYAML panics at init on malformed YAML. Programmer errors +// (malformed embedded YAML at startup) fail loud; production never sees them. +func MustLoadToolsYAML(data []byte) []ToolDefinition { + tools, err := LoadToolsYAML(data) + if err != nil { + panic(err) + } + return tools +} diff --git a/tools_test.go b/tools_test.go new file mode 100644 index 00000000..0fdf3c35 --- /dev/null +++ b/tools_test.go @@ -0,0 +1,390 @@ +package mcp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadToolsYAML(t *testing.T) { + tests := []struct { + name string + input string + want []ToolDefinition + wantErr bool + errContains string // optional substring check on error message + }{ + { + name: "valid minimal", + input: ` +version: 1 +tools: + my_tool: + description: "Does something useful." + parameters: + query: + description: "The search query." +`, + want: []ToolDefinition{ + { + Name: ToolName("my_tool"), + Description: "Does something useful.", + Parameters: []Parameter{ + {Name: ParamName("query"), Description: "The search query."}, + }, + }, + }, + }, + { + name: "valid with required propagation", + input: ` +version: 1 +tools: + create_item: + description: "Creates an item." + parameters: + name: + description: "Item name." + required: true + color: + description: "Item color." +`, + want: []ToolDefinition{ + { + Name: ToolName("create_item"), + Description: "Creates an item.", + Parameters: []Parameter{ + {Name: ParamName("name"), Description: "Item name.", Required: true}, + {Name: ParamName("color"), Description: "Item color."}, + }, + }, + }, + }, + { + name: "missing version", + input: ` +tools: + my_tool: + description: "Does something." +`, + wantErr: true, + }, + { + name: "unsupported version", + input: ` +version: 2 +tools: + my_tool: + description: "Does something." +`, + wantErr: true, + }, + { + name: "unknown top-level key", + input: ` +version: 1 +toools: + my_tool: + description: "Does something." +`, + wantErr: true, + }, + { + name: "unknown nested key", + input: ` +version: 1 +tools: + my_tool: + descripton: "Typo in key." +`, + wantErr: true, + }, + { + name: "parameter declaration order preserved", + input: ` +version: 1 +tools: + ordered_tool: + description: "Order matters." + parameters: + zebra: + description: "Last alphabetically." + mango: + description: "Middle alphabetically." + apple: + description: "First alphabetically." +`, + want: []ToolDefinition{ + { + Name: ToolName("ordered_tool"), + Description: "Order matters.", + Parameters: []Parameter{ + {Name: ParamName("zebra"), Description: "Last alphabetically."}, + {Name: ParamName("mango"), Description: "Middle alphabetically."}, + {Name: ParamName("apple"), Description: "First alphabetically."}, + }, + }, + }, + }, + { + name: "empty parameters", + input: ` +version: 1 +tools: + no_params: + description: "No parameters needed." + parameters: {} +`, + // parameters: {} decodes as an empty MappingNode, so we get an empty non-nil slice. + want: []ToolDefinition{ + { + Name: ToolName("no_params"), + Description: "No parameters needed.", + Parameters: []Parameter{}, + }, + }, + }, + { + name: "parameters null", + input: ` +version: 1 +tools: + nullparams: + description: "Null parameters." + parameters: ~ +`, + want: []ToolDefinition{ + { + Name: ToolName("nullparams"), + Description: "Null parameters.", + Parameters: nil, + }, + }, + }, + { + name: "parameters missing", + input: ` +version: 1 +tools: + noparams: + description: "No parameters key." +`, + want: []ToolDefinition{ + { + Name: ToolName("noparams"), + Description: "No parameters key.", + Parameters: nil, + }, + }, + }, + { + name: "duplicate tool name rejected", + input: ` +version: 1 +tools: + my_tool: + description: "First." + my_tool: + description: "Second." +`, + wantErr: true, + errContains: "duplicate", + }, + { + name: "empty tools rejected", + input: ` +version: 1 +tools: {} +`, + wantErr: true, + errContains: "non-empty", + }, + { + name: "tools null rejected", + input: ` +version: 1 +tools: ~ +`, + wantErr: true, + errContains: "non-empty", + }, + { + name: "missing tools rejected", + input: ` +version: 1 +`, + wantErr: true, + errContains: "non-empty", + }, + { + name: "parameter-level typo rejected", + input: ` +version: 1 +tools: + my_tool: + description: "Does something." + parameters: + query: + description: "A query." + requird: true +`, + wantErr: true, + errContains: "requird", + }, + { + name: "parameter-level unknown key rejected", + input: ` +version: 1 +tools: + my_tool: + description: "Does something." + parameters: + query: + description: "A query." + frobnicate: foo +`, + wantErr: true, + errContains: "frobnicate", + }, + { + name: "tool-level unknown key rejected", + input: ` +version: 1 +tools: + my_tool: + description_x: "Typo at tool level." +`, + wantErr: true, + errContains: "description_x", + }, + { + name: "required false rejected", + input: ` +version: 1 +tools: + my_tool: + description: "Does something." + parameters: + query: + description: "A query." + required: false +`, + wantErr: true, + errContains: "required: false is not allowed", + }, + { + name: "empty input rejected", + input: "", + wantErr: true, + errContains: "empty", + }, + { + name: "multi-document rejected", + input: ` +version: 1 +tools: + my_tool: + description: "First doc." +--- +version: 1 +tools: + other_tool: + description: "Second doc." +`, + wantErr: true, + errContains: "multi-document", + }, + { + name: "empty description rejected", + input: ` +version: 1 +tools: + my_tool: + description: "" +`, + wantErr: true, + errContains: "description must be non-empty", + }, + { + name: "missing description rejected", + input: ` +version: 1 +tools: + my_tool: + parameters: + query: + description: "A query." +`, + wantErr: true, + errContains: "description must be non-empty", + }, + { + name: "empty parameter description rejected", + input: ` +version: 1 +tools: + my_tool: + description: "Has a tool description." + parameters: + query: + description: "" +`, + wantErr: true, + errContains: "description must be non-empty", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := LoadToolsYAML([]byte(tt.input)) + if tt.wantErr { + require.Error(t, err) + if tt.errContains != "" { + assert.Contains(t, err.Error(), tt.errContains) + } + return + } + require.NoError(t, err) + require.Len(t, got, len(tt.want)) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestLoadToolsYAML_MissingVersionIsZero(t *testing.T) { + // Version field absent decodes as 0, which is != 1 and must return an error. + input := ` +tools: + my_tool: + description: "Does something." +` + _, err := LoadToolsYAML([]byte(input)) + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported version 0") +} + +func TestMustLoadToolsYAML_PanicsOnBadYAML(t *testing.T) { + require.Panics(t, func() { + MustLoadToolsYAML([]byte("version: 99\ntools: {}")) + }) +} + +func TestLoadToolsYAML_MultiToolDeclarationOrderPreserved(t *testing.T) { + input := ` +version: 1 +tools: + zebra: + description: "Third alphabetically." + mango: + description: "Second alphabetically." + apple: + description: "First alphabetically." +` + got, err := LoadToolsYAML([]byte(input)) + require.NoError(t, err) + require.Len(t, got, 3) + assert.Equal(t, ToolName("zebra"), got[0].Name) + assert.Equal(t, ToolName("mango"), got[1].Name) + assert.Equal(t, ToolName("apple"), got[2].Name) +}