@openhands/extensions 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/AGENTS.md +1 -0
  3. package/README.md +5 -2
  4. package/automations/README.md +78 -11
  5. package/automations/bundle-index.js +18 -0
  6. package/automations/catalog/github-agents-md-maintainer/manifest.json +96 -0
  7. package/automations/catalog/github-issue-to-pr/manifest.json +107 -0
  8. package/automations/catalog/github-pr-reviewer/manifest.json +24 -7
  9. package/automations/catalog/github-repo-monitor/manifest.json +29 -34
  10. package/automations/catalog/jira-issue-to-pr/manifest.json +3 -3
  11. package/automations/catalog/news-digest/manifest.json +76 -0
  12. package/automations/catalog-index.js +13 -7
  13. package/automations/catalog.schema.json +112 -3
  14. package/automations/index.d.ts +66 -1
  15. package/automations/index.js +14 -0
  16. package/automations/interface.json +6 -2
  17. package/automations/interface.schema.json +6 -2
  18. package/index.d.ts +1 -1
  19. package/index.js +1 -1
  20. package/integrations/catalog/posthog.json +41 -3
  21. package/integrations/catalog/sonarqube.json +78 -0
  22. package/integrations/catalog-index.js +65 -63
  23. package/marketplaces/openhands-extensions.json +49 -0
  24. package/package.json +1 -1
  25. package/plugins/qa-changes/action.yml +1 -1
  26. package/pyproject.toml +1 -1
  27. package/python/openhands_extensions/_version.py +1 -1
  28. package/scripts/build-automation-catalog.mjs +30 -0
  29. package/scripts/build-skills-catalog.mjs +24 -7
  30. package/skills/add-skill/SKILL.md +1 -1
  31. package/skills/github-agents-md-maintainer/.plugin/plugin.json +19 -0
  32. package/skills/github-agents-md-maintainer/README.md +59 -0
  33. package/skills/github-agents-md-maintainer/SKILL.md +271 -0
  34. package/skills/github-agents-md-maintainer/commands/agents-md-setup.md +8 -0
  35. package/skills/github-agents-md-maintainer/references/state-schema.md +113 -0
  36. package/skills/github-agents-md-maintainer/scripts/main.py +1187 -0
  37. package/skills/github-issue-to-pr/.plugin/plugin.json +19 -0
  38. package/skills/github-issue-to-pr/README.md +84 -0
  39. package/skills/github-issue-to-pr/SKILL.md +373 -0
  40. package/skills/github-issue-to-pr/commands/issue-to-pr-setup.md +8 -0
  41. package/skills/github-issue-to-pr/references/state-schema.md +139 -0
  42. package/skills/github-issue-to-pr/scripts/main.py +1305 -0
  43. package/skills/github-pr-reviewer/README.md +12 -6
  44. package/skills/github-pr-reviewer/SKILL.md +93 -31
  45. package/skills/github-pr-reviewer/commands/pr-reviewer-setup.md +1 -1
  46. package/skills/github-pr-reviewer/references/state-schema.md +68 -18
  47. package/skills/github-pr-reviewer/scripts/main.py +540 -103
  48. package/skills/github-pr-reviewer/tests/test_main.py +503 -0
  49. package/skills/index.d.ts +6 -0
  50. package/skills/index.js +56 -17
  51. package/skills/jira-issue-to-pr/scripts/main.py +4 -2
  52. package/skills/news-digest/.plugin/plugin.json +19 -0
  53. package/skills/news-digest/README.md +71 -0
  54. package/skills/news-digest/SKILL.md +284 -0
  55. package/skills/news-digest/commands/news-digest-setup.md +8 -0
  56. package/skills/news-digest/references/state-schema.md +178 -0
  57. package/skills/news-digest/scripts/main.py +1250 -0
  58. package/skills/openhands-sdk/SKILL.md +2 -0
  59. package/skills/skill-creator/SKILL.md +1 -1
  60. package/skills/slack-channel-monitor/scripts/main.py +3 -2
  61. package/tests/fixtures/automations/capabilities.json +30 -0
  62. package/tests/fixtures/automations/github-pr-reviewer.json +319 -55
  63. package/tests/fixtures/automations/github-repo-monitor.json +230 -62
  64. package/tests/test_automation_setup.py +260 -31
  65. package/tests/test_catalogs.py +46 -1
  66. package/tests/test_github_agents_md_maintainer.py +352 -0
  67. package/tests/test_github_issue_to_pr.py +539 -0
  68. package/tests/test_news_digest.py +646 -0
  69. package/tests/test_skills_catalog.py +182 -36
  70. package/tests/test_slack_channel_monitor.py +1 -1
@@ -21,8 +21,17 @@
21
21
  "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
22
22
  "description": "Stable identifier. Must equal the name of the directory holding this manifest."
23
23
  },
24
+ "version": {
25
+ "type": "string",
26
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
27
+ "description": "Semantic version of this template. Present only on entries whose direct setup sends provenance to the automation service; bump it when the prompt, form, or filter changes meaningfully."
28
+ },
24
29
  "name": { "type": "string", "minLength": 1 },
25
30
  "category": { "type": "string", "minLength": 1 },
31
+ "icon": {
32
+ "description": "The glyph the card shows, named from the same closed set the interface manifest's navigation uses. Absent means the host derives one from requires.integrations, which is right for an automation whose identity is the service it talks to and wrong for one whose identity is the work itself.",
33
+ "enum": ["layout-dashboard", "sparkles", "bot", "circle-alert", "activity", "timer"]
34
+ },
26
35
  "description": { "type": "string", "minLength": 1 },
27
36
  "requires": { "$ref": "#/$defs/requires" },
28
37
  "popularityRank": { "type": "integer", "minimum": 0 },
@@ -63,6 +72,10 @@
63
72
  "description": "direct only. What the automation is told to do. The rest of the create request restates the form, so it is not written here: the automation's name comes from this entry, the repository from the repo-picker field, and the trigger from the key and fields under form.triggers.",
64
73
  "$ref": "#/$defs/templateValue"
65
74
  },
75
+ "bundle": {
76
+ "description": "direct only, and the alternative to prompt. The script tarball this entry ships: the automation is deterministic machinery rather than judgement, so it runs as its own code and the host creates it through the raw create endpoint instead of a preset. The rest of the create request still restates the form, so it is not written here either.",
77
+ "$ref": "#/$defs/bundle"
78
+ },
66
79
  "filter": {
67
80
  "description": "direct only, and only for an event trigger. The expression deciding whether a delivered event belongs to this automation. It composes form values into a JMESPath expression, which is the one part of an event trigger that cannot be read off the form.",
68
81
  "$ref": "#/$defs/templateValue"
@@ -79,7 +92,10 @@
79
92
  "required": ["mode"]
80
93
  },
81
94
  "then": {
82
- "required": ["prompt"]
95
+ "oneOf": [
96
+ { "required": ["prompt"], "not": { "required": ["bundle"] } },
97
+ { "required": ["bundle"], "not": { "required": ["prompt"] } }
98
+ ]
83
99
  }
84
100
  },
85
101
  {
@@ -91,6 +107,7 @@
91
107
  "required": ["message"],
92
108
  "allOf": [
93
109
  { "not": { "required": ["prompt"] } },
110
+ { "not": { "required": ["bundle"] } },
94
111
  { "not": { "required": ["filter"] } }
95
112
  ]
96
113
  }
@@ -108,6 +125,88 @@
108
125
  "required": ["form"]
109
126
  },
110
127
  "then": { "not": { "required": ["filter"] } }
128
+ },
129
+ {
130
+ "if": { "required": ["bundle"] },
131
+ "then": { "not": { "required": ["filter"] } }
132
+ }
133
+ ]
134
+ },
135
+ "bundle": {
136
+ "description": "What the host packs into a .tar.gz and uploads before creating the automation. It names files inside this repository and the command that runs them - never a host, a URL, or code to evaluate.",
137
+ "type": "object",
138
+ "additionalProperties": false,
139
+ "required": ["version", "entrypoint", "files", "config"],
140
+ "properties": {
141
+ "version": {
142
+ "description": "The version of this bundle, recorded as the created automation's template provenance. Bump it when the shipped files or the config shape change: it is what tells an already-enabled deployment that what it installed is no longer current. Not the package version, which moves for reasons that have nothing to do with this entry.",
143
+ "type": "string",
144
+ "minLength": 1,
145
+ "maxLength": 50,
146
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
147
+ },
148
+ "entrypoint": {
149
+ "description": "The command the automation service runs inside the extracted tarball, such as `python3 main.py`. No shell metacharacters: the service rejects them, and a bundle has no reason to need one.",
150
+ "type": "string",
151
+ "minLength": 1,
152
+ "maxLength": 500,
153
+ "pattern": "^[A-Za-z0-9 ._/-]+$"
154
+ },
155
+ "setupScript": {
156
+ "description": "Path inside the tarball to a script run once before the entrypoint, such as `setup.sh`. Absent when the bundle needs no installation step, which is the case for a standard-library script.",
157
+ "type": "string",
158
+ "minLength": 1,
159
+ "maxLength": 255,
160
+ "pattern": "^[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
161
+ "not": { "pattern": "(^|/)\\.\\.?(/|$)" }
162
+ },
163
+ "timeout": {
164
+ "description": "Seconds a single run may take, when this entry needs more than the service default. A poll that fetches an archive per queued item needs longer than one that only calls an API.",
165
+ "type": "integer",
166
+ "minimum": 1,
167
+ "maximum": 86400
168
+ },
169
+ "files": {
170
+ "description": "The tarball's contents, keyed by the path each file takes inside the archive and valued by where it lives in this repository. Stated rather than derived from the entry directory, so a script shipped by both a skill and the catalog has one copy rather than two.",
171
+ "type": "object",
172
+ "minProperties": 1,
173
+ "maxProperties": 32,
174
+ "propertyNames": {
175
+ "pattern": "^[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
176
+ "not": { "pattern": "(^|/)\\.\\.?(/|$)" },
177
+ "maxLength": 255
178
+ },
179
+ "additionalProperties": { "$ref": "#/$defs/bundleSource" }
180
+ },
181
+ "config": {
182
+ "description": "The config.json packed beside the entrypoint, rendered from the form. This is the bundle's analogue of prompt: the one thing that cannot be read off the form, because only the entry knows which key of its own script each field fills.",
183
+ "$ref": "#/$defs/bundleConfig"
184
+ }
185
+ }
186
+ },
187
+ "bundleSource": {
188
+ "description": "A file in this repository, as a repository-relative path under skills/ or automations/. No absolute path, no traversal, and no scheme, so a bundle cannot name anything outside the published package.",
189
+ "type": "string",
190
+ "minLength": 1,
191
+ "maxLength": 255,
192
+ "pattern": "^(skills|automations)/[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
193
+ "not": { "pattern": "(^|/)\\.\\.?(/|$)" }
194
+ },
195
+ "bundleConfig": {
196
+ "description": "The JSON document written to config.json. Its string leaves may embed the same placeholders every other value uses. It carries no markup rule, because it is written to a file rather than rendered.",
197
+ "type": "object",
198
+ "minProperties": 1,
199
+ "additionalProperties": { "$ref": "#/$defs/bundleConfigValue" }
200
+ },
201
+ "bundleConfigValue": {
202
+ "description": "A config leaf: a templated string, a number, a boolean, null, or an array or object of the same.",
203
+ "anyOf": [
204
+ { "$ref": "#/$defs/templateValue" },
205
+ { "type": ["number", "boolean", "null"] },
206
+ { "type": "array", "items": { "$ref": "#/$defs/bundleConfigValue" } },
207
+ {
208
+ "type": "object",
209
+ "additionalProperties": { "$ref": "#/$defs/bundleConfigValue" }
111
210
  }
112
211
  ]
113
212
  },
@@ -143,9 +242,8 @@
143
242
  "required": ["integrations"],
144
243
  "properties": {
145
244
  "integrations": {
146
- "description": "Keyed by integration id, each of which must resolve to an integrations/catalog entry.",
245
+ "description": "Keyed by integration id, each of which must resolve to an integrations/catalog entry. Empty when the automation connects to nothing and needs no credential - a requirement worth stating, and the reason the key stays required rather than becoming optional.",
147
246
  "type": "object",
148
- "minProperties": 1,
149
247
  "propertyNames": { "type": "string", "minLength": 1 },
150
248
  "additionalProperties": {
151
249
  "type": "object",
@@ -224,6 +322,10 @@
224
322
  "default": { "type": "string" },
225
323
  "required": { "type": "boolean" },
226
324
  "provider": { "enum": ["github", "gitlab", "bitbucket"] },
325
+ "multiple": {
326
+ "description": "repo-picker only. The field collects several repositories rather than one, and its value is a list. A whole-value placeholder resolves to that list, so a payload can state `\"repos\": \"{{form.repositories}}\"` and get an array.",
327
+ "const": true
328
+ },
227
329
  "options": {
228
330
  "type": "array",
229
331
  "minItems": 1,
@@ -259,6 +361,13 @@
259
361
  },
260
362
  "then": { "required": ["provider"] }
261
363
  },
364
+ {
365
+ "if": {
366
+ "properties": { "type": { "not": { "const": "repo-picker" } } },
367
+ "required": ["type"]
368
+ },
369
+ "then": { "not": { "required": ["multiple"] } }
370
+ },
262
371
  {
263
372
  "if": {
264
373
  "properties": { "type": { "not": { "const": "select" } } },
@@ -1,7 +1,24 @@
1
1
  export interface RecommendedAutomation {
2
2
  id: string;
3
+ /**
4
+ * Semantic version of this template. Present only on entries whose direct
5
+ * setup sends provenance to the automation service; bumped when the prompt,
6
+ * form, or filter changes meaningfully.
7
+ */
8
+ version?: string;
3
9
  name: string;
4
10
  category: string;
11
+ /**
12
+ * The glyph the card shows, from the closed set the interface manifest's
13
+ * navigation uses. Absent means the host derives one from the integrations.
14
+ */
15
+ icon?:
16
+ | "layout-dashboard"
17
+ | "sparkles"
18
+ | "bot"
19
+ | "circle-alert"
20
+ | "activity"
21
+ | "timer";
5
22
  description: string;
6
23
  requires: AutomationPrerequisites;
7
24
  popularityRank: number;
@@ -63,6 +80,11 @@ export interface AutomationFormField {
63
80
  default?: string;
64
81
  required: boolean;
65
82
  provider?: AutomationGitProvider;
83
+ /**
84
+ * repo-picker only. The field collects several repositories rather than one,
85
+ * and its value is a list. A whole-value placeholder resolves to that list.
86
+ */
87
+ multiple?: true;
66
88
  options?: AutomationFieldOption[];
67
89
  constraints?: AutomationFieldConstraints;
68
90
  }
@@ -78,7 +100,7 @@ export interface AutomationIntegrationRequirement {
78
100
  }
79
101
 
80
102
  export interface AutomationPrerequisites {
81
- /** Keyed by integration catalog id. */
103
+ /** Keyed by integration catalog id. Empty when the automation needs nothing connected. */
82
104
  integrations: Record<string, AutomationIntegrationRequirement>;
83
105
  /** Deployment capabilities this automation cannot run without. */
84
106
  features?: string[];
@@ -96,12 +118,40 @@ export interface AutomationForm {
96
118
  args: AutomationFormFields;
97
119
  }
98
120
 
121
+ /** A config.json leaf: templated string, number, boolean, null, or a nesting of those. */
122
+ export type AutomationBundleConfigValue =
123
+ | string
124
+ | number
125
+ | boolean
126
+ | null
127
+ | AutomationBundleConfigValue[]
128
+ | { [key: string]: AutomationBundleConfigValue };
129
+
130
+ /**
131
+ * The script tarball a direct entry may ship instead of a prompt, for an
132
+ * automation that is deterministic machinery rather than judgement.
133
+ */
134
+ export interface AutomationBundle {
135
+ /** The command run inside the extracted tarball. */
136
+ entrypoint: string;
137
+ /** Script run once before the entrypoint. Absent when nothing to install. */
138
+ setupScript?: string;
139
+ /** Seconds a run may take, when the service default is not enough. */
140
+ timeout?: number;
141
+ /** Packed path -> the repository path the file is read from at build time. */
142
+ files: Record<string, string>;
143
+ /** Rendered from the form and packed as config.json beside the entrypoint. */
144
+ config: Record<string, AutomationBundleConfigValue>;
145
+ }
146
+
99
147
  export interface AutomationSetup {
100
148
  version: "1.0";
101
149
  mode: AutomationSetupMode;
102
150
  form: AutomationForm;
103
151
  /** direct only. What the automation is told to do. */
104
152
  prompt?: string;
153
+ /** direct only, and the alternative to `prompt`. Exactly one is present. */
154
+ bundle?: AutomationBundle;
105
155
  /** direct only, event trigger only. Which delivered events belong to it. */
106
156
  filter?: string;
107
157
  /**
@@ -337,6 +387,10 @@ export interface AutomationInterfaceEndpoints {
337
387
  validate: string;
338
388
  createPrompt: string;
339
389
  createPlugin: string;
390
+ /** The raw create endpoint, which a bundle entry is created through. */
391
+ createBundle: string;
392
+ /** Where a bundle's tarball is uploaded before that create call. */
393
+ uploads: string;
340
394
  }
341
395
 
342
396
  export interface AutomationInterfaceManifest {
@@ -365,4 +419,15 @@ export function listAutomationCatalog(): RecommendedAutomation[];
365
419
  export function getAutomationCatalogEntry(
366
420
  id: string,
367
421
  ): RecommendedAutomation | undefined;
422
+ /**
423
+ * Return the files a bundle entry ships, keyed by the path each takes inside
424
+ * the tarball, as an independent copy. Undefined for an entry with no bundle.
425
+ *
426
+ * The contents are inlined at build time from the repository paths
427
+ * `setup.bundle.files` names, because a host packing the tarball has the
428
+ * published package but not the repository.
429
+ */
430
+ export function getAutomationBundleFiles(
431
+ id: string,
432
+ ): Record<string, string> | undefined;
368
433
  export default AUTOMATION_CATALOG;
@@ -5,6 +5,7 @@
5
5
  * directory. `catalog-index.js` is generated from that directory so the JS
6
6
  * package can statically import each JSON file without an aggregate JSON asset.
7
7
  */
8
+ import { AUTOMATION_BUNDLE_FILES } from "./bundle-index.js";
8
9
  import { AUTOMATION_CATALOG_ENTRIES } from "./catalog-index.js";
9
10
  import interfaceManifest from "./interface.json" with { type: "json" };
10
11
 
@@ -21,6 +22,19 @@ export const getAutomationCatalogEntry = (id) => {
21
22
 
22
23
  export const AUTOMATION_CATALOG = clone(AUTOMATIONS);
23
24
 
25
+ /**
26
+ * The files a bundle entry ships, keyed by entry id and then by the path each
27
+ * file takes inside the tarball. Generated from the repository paths the
28
+ * entry's `setup.bundle.files` names, because a host packing the tarball has
29
+ * the package but not the repository.
30
+ *
31
+ * Absent for every entry that does not ship a bundle.
32
+ */
33
+ export const getAutomationBundleFiles = (id) => {
34
+ const files = AUTOMATION_BUNDLE_FILES[id];
35
+ return files ? clone(files) : undefined;
36
+ };
37
+
24
38
  /**
25
39
  * The production Automation interface manifest: the domain-level facts of the
26
40
  * interface, hand-authored in `automations/interface.json`.
@@ -144,11 +144,15 @@
144
144
  "capabilities": "/v1/capabilities",
145
145
  "validate": "/v1/validate",
146
146
  "createPrompt": "/v1/preset/prompt",
147
- "createPlugin": "/v1/preset/plugin"
147
+ "createPlugin": "/v1/preset/plugin",
148
+ "createBundle": "/v1",
149
+ "uploads": "/v1/uploads"
148
150
  },
149
151
  "featuredAutomationIds": [
150
152
  "github-pr-reviewer",
151
- "github-repo-monitor",
153
+ "github-agents-md-maintainer",
154
+ "news-digest",
155
+ "github-issue-to-pr",
152
156
  "slack-channel-monitor"
153
157
  ],
154
158
  "responderIntegrationIds": ["github", "slack"]
@@ -178,7 +178,9 @@
178
178
  "capabilities",
179
179
  "validate",
180
180
  "createPrompt",
181
- "createPlugin"
181
+ "createPlugin",
182
+ "createBundle",
183
+ "uploads"
182
184
  ],
183
185
  "properties": {
184
186
  "list": { "$ref": "#/$defs/plainEndpoint" },
@@ -190,7 +192,9 @@
190
192
  "capabilities": { "$ref": "#/$defs/plainEndpoint" },
191
193
  "validate": { "$ref": "#/$defs/plainEndpoint" },
192
194
  "createPrompt": { "$ref": "#/$defs/plainEndpoint" },
193
- "createPlugin": { "$ref": "#/$defs/plainEndpoint" }
195
+ "createPlugin": { "$ref": "#/$defs/plainEndpoint" },
196
+ "createBundle": { "$ref": "#/$defs/plainEndpoint" },
197
+ "uploads": { "$ref": "#/$defs/plainEndpoint" }
194
198
  }
195
199
  },
196
200
  "featuredAutomationIds": {
package/index.d.ts CHANGED
@@ -42,5 +42,5 @@ export type {
42
42
  AutomationInterfacePages,
43
43
  AutomationInterfaceRoutes,
44
44
  } from "./automations/index.js";
45
- export { SKILLS_CATALOG } from "./skills/index.js";
45
+ export { SKILLS_CATALOG, DEFAULT_ENABLED_SKILL_NAMES } from "./skills/index.js";
46
46
  export type { SkillCatalogEntry } from "./skills/index.js";
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { INTEGRATION_CATALOG } from "./integrations/index.js";
2
2
  export { AUTOMATION_CATALOG, AUTOMATION_INTERFACE } from "./automations/index.js";
3
- export { SKILLS_CATALOG } from "./skills/index.js";
3
+ export { SKILLS_CATALOG, DEFAULT_ENABLED_SKILL_NAMES } from "./skills/index.js";
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "appUrl": "https://posthog.com",
10
10
  "docsUrl": "https://posthog.com/docs/model-context-protocol",
11
- "notes": "Relevant for product analytics and experimentation workflows.",
11
+ "notes": "Uses PostHog's official hosted MCP server. OAuth is preferred; the bearer-token option remains for clients or deployments that require a personal API key. The OAuth URL defaults to read-only mode; remove readonly=true only when write access has been approved.",
12
12
  "popularityRank": 37,
13
13
  "iconBg": "#1D4AFF",
14
14
  "logoUrl": "https://cdn.simpleicons.org/posthog/FFFFFF",
@@ -18,8 +18,44 @@
18
18
  "product",
19
19
  "experiments"
20
20
  ],
21
- "installHint": "Authenticate with a PostHog personal API key (PostHog → User settings → API keys, MCP Server preset) — sent as a Bearer token.",
21
+ "installHint": "Prefer OAuth. The default OAuth connection uses read-only MCP mode; edit the URL or use the bearer-key fallback only when a workflow needs approved write access. For API keys, create a PostHog personal API key with the MCP Server preset.",
22
22
  "connectionOptions": [
23
+ {
24
+ "id": "oauth",
25
+ "provider": "mcp",
26
+ "auth": {
27
+ "strategy": "oauth2",
28
+ "oauth": {
29
+ "authorizationUrl": "https://oauth.posthog.com/oauth/authorize/",
30
+ "tokenUrl": "https://oauth.posthog.com/oauth/token/",
31
+ "registrationUrl": "https://oauth.posthog.com/oauth/register/",
32
+ "scopes": [
33
+ "openid",
34
+ "profile",
35
+ "email",
36
+ "organization:read",
37
+ "project:read",
38
+ "dashboard:read",
39
+ "insight:read",
40
+ "query:read",
41
+ "event_definition:read"
42
+ ],
43
+ "pkce": true,
44
+ "clientAuthentication": "none",
45
+ "additionalAuthorizationParams": {
46
+ "resource": "https://mcp.posthog.com/mcp"
47
+ },
48
+ "additionalTokenParams": {
49
+ "resource": "https://mcp.posthog.com/mcp"
50
+ }
51
+ }
52
+ },
53
+ "transport": {
54
+ "kind": "shttp",
55
+ "url": "https://mcp.posthog.com/mcp?readonly=true",
56
+ "urlEditable": true
57
+ }
58
+ },
23
59
  {
24
60
  "id": "api-key",
25
61
  "provider": "mcp",
@@ -31,7 +67,9 @@
31
67
  "strategy": "bearer",
32
68
  "credentialLabel": "PostHog personal API key",
33
69
  "credentialPlaceholder": "Paste your PostHog personal API key",
34
- "credentialHelp": "Create one under PostHog → User settings → API keys (MCP Server preset). Sent as Authorization: Bearer <token>."
70
+ "credentialHelp": "Create one under PostHog > User settings > API keys using the MCP Server preset. Sent as Authorization: Bearer <token>.",
71
+ "credentialSecretName": "POSTHOG_PERSONAL_API_KEY",
72
+ "saveCredentialAsSecretByDefault": true
35
73
  }
36
74
  }
37
75
  ]
@@ -0,0 +1,78 @@
1
+ {
2
+ "id": "sonarqube",
3
+ "name": "SonarQube",
4
+ "description": "Inspect code quality and security findings, quality gates, and analyze code snippets from SonarQube Server or Cloud.",
5
+ "categories": [
6
+ "Engineering",
7
+ "Security"
8
+ ],
9
+ "appUrl": "https://www.sonarsource.com/products/sonarqube/mcp-server/",
10
+ "docsUrl": "https://github.com/SonarSource/sonarqube-mcp-server",
11
+ "notes": "Uses SonarSource's official SonarQube MCP Server, run locally as a Docker container. Provide a SonarQube user token. Set SONARQUBE_ORG for SonarQube Cloud, or SONARQUBE_URL for a self-managed SonarQube Server (or https://sonarqube.us for SonarQube Cloud US).",
12
+ "popularityRank": 43,
13
+ "installHint": "SONARQUBE_TOKEN is always required. For SonarQube Cloud, set your organization key in SONARQUBE_ORG. For a self-managed SonarQube Server, set SONARQUBE_URL to your server URL; for SonarQube Cloud US, set SONARQUBE_URL to https://sonarqube.us. Requires a container runtime such as Docker.",
14
+ "connectionOptions": [
15
+ {
16
+ "id": "api",
17
+ "provider": "mcp",
18
+ "transport": {
19
+ "kind": "stdio",
20
+ "serverName": "sonarqube",
21
+ "command": "docker",
22
+ "args": [
23
+ "run",
24
+ "--init",
25
+ "--pull=always",
26
+ "-i",
27
+ "--rm",
28
+ "-e",
29
+ "SONARQUBE_TOKEN",
30
+ "-e",
31
+ "SONARQUBE_ORG",
32
+ "-e",
33
+ "SONARQUBE_URL",
34
+ "sonarsource/sonarqube-mcp"
35
+ ],
36
+ "envFields": [
37
+ {
38
+ "key": "SONARQUBE_TOKEN",
39
+ "label": "SonarQube token",
40
+ "type": "password",
41
+ "required": true,
42
+ "helperText": "User token generated in your SonarQube Server or Cloud account settings.",
43
+ "helperLink": "https://docs.sonarsource.com/sonarqube-cloud/managing-your-account/managing-tokens/"
44
+ },
45
+ {
46
+ "key": "SONARQUBE_ORG",
47
+ "label": "Organization key (SonarQube Cloud)",
48
+ "type": "text",
49
+ "required": false,
50
+ "placeholder": "my-org",
51
+ "helperText": "Required for SonarQube Cloud. Your organization key, found in SonarQube Cloud."
52
+ },
53
+ {
54
+ "key": "SONARQUBE_URL",
55
+ "label": "Server URL (SonarQube Server)",
56
+ "type": "text",
57
+ "required": false,
58
+ "placeholder": "https://sonarqube.example.com",
59
+ "helperText": "Required for a self-managed SonarQube Server. For SonarQube Cloud US, use https://sonarqube.us."
60
+ }
61
+ ]
62
+ },
63
+ "auth": {
64
+ "strategy": "api_key"
65
+ }
66
+ }
67
+ ],
68
+ "iconBg": "#126ED3",
69
+ "logoUrl": "https://cdn.simpleicons.org/sonarqubeserver/FFFFFF",
70
+ "keywords": [
71
+ "code quality",
72
+ "static analysis",
73
+ "security",
74
+ "sast",
75
+ "quality gate",
76
+ "sonarcloud"
77
+ ]
78
+ }