Skip to content

Commit a361af2

Browse files
- Added an "Append API Key to URL" block setting to the MCP Server List block. When disabled (the default), the API key is no longer included in the displayed MCP URL and the security notice is hidden.
1 parent c2cd8af commit a361af2

4 files changed

Lines changed: 57 additions & 10 deletions

File tree

Rock.Blocks/Core/McpServerList.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,42 @@ namespace Rock.Blocks.Core
4040
[IconCssClass( "ti ti-robot" )]
4141
[SupportedSiteTypes( SiteType.Web )]
4242

43+
[BooleanField( "Append API Key to URL",
44+
Description = "When enabled, the individual's API key is appended to the MCP URL. Use this if the MCP server requires authentication via URL parameter rather than using OAuth. Note that API keys grant access based on the permissions of the individual they belong to — treat them as sensitive credentials and avoid sharing or exposing MCP URLs that contain them.",
45+
DefaultBooleanValue = false,
46+
Order = 0,
47+
Key = AttributeKey.AppendApiKeyToUrl )]
48+
4349
[Rock.Cms.DefaultBlockRole( Rock.Enums.Cms.BlockRole.Primary )]
4450
[Rock.SystemGuid.EntityTypeGuid( "F0B14291-8035-4986-A4D8-DC1AE08E4F7B" )]
4551
[Rock.SystemGuid.BlockTypeGuid( "54B23A63-87C0-4955-B915-C91F23C36D48" )]
4652
public class McpServerList : RockBlockType
4753
{
54+
#region Keys
55+
56+
private static class AttributeKey
57+
{
58+
public const string AppendApiKeyToUrl = "AppendApiKeyToUrl";
59+
}
60+
61+
#endregion
62+
4863
#region Methods
4964

5065
public override object GetObsidianBlockInitialization()
5166
{
67+
var appendApiKeyToUrl = GetAttributeValue( AttributeKey.AppendApiKeyToUrl ).AsBoolean();
68+
5269
var box = new InitializationBox
5370
{
54-
Items = GetMcpServers()
71+
Items = GetMcpServers( appendApiKeyToUrl ),
72+
IsApiKeyAppendedToUrl = appendApiKeyToUrl
5573
};
5674

5775
return box;
5876
}
5977

60-
private List<McpServerListItemBag> GetMcpServers()
78+
private List<McpServerListItemBag> GetMcpServers( bool appendApiKeyToUrl )
6179
{
6280
var mcpAiAgents = AIAgentCache.All()
6381
.Where( a => a.AgentType == AgentType.Mcp )
@@ -84,21 +102,27 @@ private List<McpServerListItemBag> GetMcpServers()
84102

85103
var publicApplicationRoot = GlobalAttributesCache.Get().GetValue( "PublicApplicationRoot" ).RemoveTrailingForwardslash();
86104

87-
// Create an API Key on block load instead of waiting for the individual to click the Copy URL button.
88-
// Doing so here will place the sensitive API Keys in the page's HTML.
89-
// If done in a block action, the API Key would be included in the API response which could be logged and
105+
// Only create/fetch an API Key when the block is configured to append it to the URL.
106+
// When appending, place the API Key in the page's HTML rather than returning it from a block action.
107+
// A block action would include the API Key in an API response which could be logged and
90108
// would be more easily accessible to users inspecting network requests,
91109
// but including it in the page's HTML means it is less likely to be accidentally exposed in logs and is not included in API responses.
92-
var apiKey = Types.Mobile.Cms.VoiceAgent.GetOrCreateMcpApiKeyForCurrentPerson( GetCurrentPerson(), RockContext );
110+
var apiKey = appendApiKeyToUrl
111+
? Types.Mobile.Cms.VoiceAgent.GetOrCreateMcpApiKeyForCurrentPerson( GetCurrentPerson(), RockContext )
112+
: null;
93113

94114
return mcpAiAgents
95115
.Select( aa => new McpServerListItemBag
96116
{
97117
AudienceType = aa.AudienceType,
98118
Name = aa.Name,
99119
Description = aa.Description,
100-
PartialUrl = $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}...",
101-
FullUrl = $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}?apikey={apiKey}",
120+
PartialUrl = appendApiKeyToUrl
121+
? $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}..."
122+
: $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}",
123+
FullUrl = appendApiKeyToUrl
124+
? $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}?apikey={apiKey}"
125+
: $"{publicApplicationRoot}/api/v2/mcp/{aa.Slug}",
102126
} )
103127
.ToList();
104128
}

Rock.JavaScript.Obsidian.Blocks/src/Core/mcpServerList.obs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</template>
66

77
<template v-if="items.length">
8-
<NotificationBox alertType="warning">
8+
<NotificationBox v-if="isApiKeyAppendedToUrl" alertType="warning">
99
<p><strong>Security Notice</strong></p>
1010
Do not share the MCP links below. They are tied directly to your user account and permissions — sharing them could compromise your account and expose sensitive data.
1111
</NotificationBox>
@@ -47,21 +47,32 @@
4747
import NotificationBox from "@Obsidian/Controls/notificationBox.obs";
4848
import Panel from "@Obsidian/Controls/panel.obs";
4949
import { AudienceType, AudienceTypeDescription } from "@Obsidian/Enums/AI/Agent/audienceType";
50-
import { useConfigurationValues } from "@Obsidian/Utility/block";
50+
import { onConfigurationValuesChanged, useConfigurationValues, useReloadBlock } from "@Obsidian/Utility/block";
5151
import { InitializationBox } from "@Obsidian/ViewModels/Blocks/Core/McpServerList/initializationBox";
5252
import { McpServerListItemBag } from "@Obsidian/ViewModels/Blocks/Core/McpServerList/mcpServerListItemBag";
5353

5454
const config = useConfigurationValues<InitializationBox>();
55+
const reloadBlock = useReloadBlock();
56+
57+
// #region Computed Values
5558

5659
const items = computed<McpServerListItemBag[]>(() => {
5760
return config.items || [];
5861
});
5962

63+
const isApiKeyAppendedToUrl = computed<boolean>(() => {
64+
return config.isApiKeyAppendedToUrl === true;
65+
});
66+
6067
const panelLabel = computed<string>(() => {
6168
if (items.value.length === 0) {
6269
return "No MCP Servers";
6370
}
6471

6572
return `${items.value.length} MCP Server${items.value.length === 1 ? "" : "s"}`;
6673
});
74+
75+
// #endregion Computed Values
76+
77+
onConfigurationValuesChanged(reloadBlock);
6778
</script>

Rock.JavaScript.Obsidian/Framework/ViewModels/Blocks/Core/McpServerList/initializationBox.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ export type InitializationBox = {
3131
*/
3232
errorMessage?: string | null;
3333

34+
/**
35+
* Gets or sets a value indicating whether the individual's API key is appended to the MCP URL.
36+
* When true, the URL contains a sensitive credential and a security notice should be displayed.
37+
*/
38+
isApiKeyAppendedToUrl: boolean;
39+
3440
/** Gets or sets the collection of MCP servers. */
3541
items?: McpServerListItemBag[] | null;
3642

Rock.ViewModels/Blocks/Core/McpServerList/InitializationBox.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,11 @@ public class InitializationBox : BlockBox
2727
/// Gets or sets the collection of MCP servers.
2828
/// </summary>
2929
public List<McpServerListItemBag> Items { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets a value indicating whether the individual's API key is appended to the MCP URL.
33+
/// When <c>true</c>, the URL contains a sensitive credential and a security notice should be displayed.
34+
/// </summary>
35+
public bool IsApiKeyAppendedToUrl { get; set; }
3036
}
3137
}

0 commit comments

Comments
 (0)