Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ef9f0a6
support mcp resources
afsalthaj Mar 3, 2026
cc5cac4
support mcp resources
afsalthaj Mar 3, 2026
5e2a05e
rename invoke functions in mcp
afsalthaj Mar 3, 2026
abb8cb6
rename invoke functions in mcp
afsalthaj Mar 3, 2026
ba179c2
Merge branch 'main' into resource_support
afsalthaj Mar 5, 2026
4ccd40e
clean up
afsalthaj Mar 5, 2026
494a58a
reformat
afsalthaj Mar 5, 2026
6a3a3c2
make tools consistent with resources
afsalthaj Mar 5, 2026
a669faa
handle unstructured binary
afsalthaj Mar 5, 2026
271422a
handle unstructured text
afsalthaj Mar 5, 2026
39b9fc3
remove the need of generic
afsalthaj Mar 5, 2026
c9316f0
rename
afsalthaj Mar 5, 2026
77ef3a6
update multimodal
afsalthaj Mar 5, 2026
c67ed8e
fix multimodal
afsalthaj Mar 5, 2026
d9beea7
reformat
afsalthaj Mar 9, 2026
7957bc2
fix multimodal invokes
afsalthaj Mar 10, 2026
3fdf139
fix bugs
afsalthaj Mar 10, 2026
2fb66bb
fix more bugs
afsalthaj Mar 10, 2026
c0a863e
fix more bugs
afsalthaj Mar 10, 2026
87fa89c
add integration tests
afsalthaj Mar 11, 2026
753a971
license docs
afsalthaj Mar 11, 2026
9dbfb44
fix some more bugs related to tool results
afsalthaj Mar 11, 2026
d5489db
fix a lot of more bugs after more testing with different MCP clients
afsalthaj Mar 11, 2026
61a2d51
reformat code
afsalthaj Mar 11, 2026
26c7fe1
fix multimodal invokes
afsalthaj Mar 11, 2026
ab15503
fix integration tests
afsalthaj Mar 11, 2026
fe1c31a
Merge branch 'main' into resource_support
afsalthaj Mar 11, 2026
5519dfa
resolve conflicts
afsalthaj Mar 11, 2026
92e28f2
resolve conflicts
afsalthaj Mar 11, 2026
c16dedc
fix integration tests
afsalthaj Mar 11, 2026
f36e506
make sure all tests pass
afsalthaj Mar 11, 2026
d3b3a80
add tests for invoke modules
afsalthaj Mar 11, 2026
20bc4d6
fix test component and update build scripts
afsalthaj Mar 11, 2026
d4e7159
Merge branch 'main' into resource_support
afsalthaj Mar 12, 2026
6f6be23
Merge branch 'main' into resource_support
afsalthaj Mar 12, 2026
18ad958
Add prompts in MCP (#2961)
afsalthaj Mar 13, 2026
2dbc155
Merge branch 'main' into resource_support
afsalthaj Mar 14, 2026
df3ab6b
Make sure unstructured-binary and multimodal are rendered correctly w…
afsalthaj Mar 14, 2026
d27bde5
Fix unit tests (#2989)
afsalthaj Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions golem-worker-service/src/mcp/agent_mcp_capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::mcp::agent_mcp_resource::AgentMcpResource;
use crate::mcp::agent_mcp_resource::{AgentMcpResource, AgentMcpResourceKind};
use crate::mcp::agent_mcp_tool::AgentMcpTool;
use crate::mcp::schema::{McpToolSchema, get_mcp_schema, get_mcp_tool_schema};
use golem_common::base_model::account::AccountId;
use golem_common::base_model::agent::{AgentMethod, AgentTypeName, DataSchema};
use golem_common::base_model::component::ComponentId;
use golem_common::base_model::environment::EnvironmentId;
use golem_common::model::agent::AgentConstructor;
use rmcp::model::Tool;
use rmcp::model::{Annotated, RawResource, RawResourceTemplate, Tool};
use std::borrow::Cow;
use std::sync::Arc;

#[derive(Clone)]
pub enum McpAgentCapability {
Tool(Box<AgentMcpTool>),
#[allow(unused)]
Resource(AgentMcpResource),
}

Expand Down Expand Up @@ -87,8 +86,55 @@ impl McpAgentCapability {
agent_type_name.0
);

let constructor_param_names =
AgentMcpResource::constructor_param_names(constructor);
let name = AgentMcpResource::resource_name(agent_type_name, method);

let kind = if constructor_param_names.is_empty() {
let uri = AgentMcpResource::static_uri(agent_type_name, method);
AgentMcpResourceKind::Static(Annotated::new(
RawResource {
uri,
name,
title: None,
description: Some(method.description.clone()),
mime_type: Some("application/json".to_string()),
size: None,
icons: None,
meta: None,
},
None,
))
} else {
let uri_template = AgentMcpResource::template_uri(
agent_type_name,
method,
&constructor_param_names,
);
AgentMcpResourceKind::Template {
template: Annotated::new(
RawResourceTemplate {
uri_template,
name,
title: None,
description: Some(method.description.clone()),
mime_type: Some("application/json".to_string()),
icons: None,
},
None,
),
constructor_param_names,
}
};

Self::Resource(AgentMcpResource {
resource: method.clone(),
kind,
environment_id: *environment_id,
account_id: *account_id,
constructor: constructor.clone(),
raw_method: method.clone(),
component_id,
agent_type_name: agent_type_name.clone(),
})
}
}
Expand Down
89 changes: 86 additions & 3 deletions golem-worker-service/src/mcp/agent_mcp_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,93 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use golem_common::base_model::agent::AgentMethod;
use golem_common::base_model::account::AccountId;
use golem_common::base_model::agent::{
AgentMethod, AgentTypeName, DataSchema, NamedElementSchemas,
};
use golem_common::base_model::component::ComponentId;
use golem_common::base_model::environment::EnvironmentId;
use golem_common::model::agent::AgentConstructor;
use rmcp::model::{Resource, ResourceTemplate};

pub type ResourceUri = String;

#[derive(Clone)]
pub struct AgentMcpResource {
#[allow(dead_code)]
pub resource: AgentMethod,
pub kind: AgentMcpResourceKind,
pub environment_id: EnvironmentId,
pub account_id: AccountId,
pub constructor: AgentConstructor,
pub raw_method: AgentMethod,
pub component_id: ComponentId,
pub agent_type_name: AgentTypeName,
}

#[derive(Clone)]
pub enum AgentMcpResourceKind {
Static(Resource),
Template {
template: ResourceTemplate,
constructor_param_names: Vec<String>,
},
}

impl AgentMcpResource {
pub fn resource_name(agent_type_name: &AgentTypeName, method: &AgentMethod) -> String {
format!("{}-{}", agent_type_name.0, method.name)
}

pub fn static_uri(agent_type_name: &AgentTypeName, method: &AgentMethod) -> String {
format!("golem://{}/{}", agent_type_name.0, method.name)
}

pub fn template_uri(
agent_type_name: &AgentTypeName,
method: &AgentMethod,
param_names: &[String],
) -> String {
let base = format!("golem://{}/{}", agent_type_name.0, method.name);
let placeholders: Vec<String> = param_names.iter().map(|n| format!("{{{}}}", n)).collect();
format!("{}/{}", base, placeholders.join("/"))
}

pub fn extract_params_from_uri(
template_uri: &str,
concrete_uri: &str,
) -> Result<Vec<(String, String)>, String> {
let template_parts: Vec<&str> = template_uri.split('/').collect();
let concrete_parts: Vec<&str> = concrete_uri.split('/').collect();

if template_parts.len() != concrete_parts.len() {
return Err(format!(
"URI segment count mismatch: template has {}, concrete has {}",
template_parts.len(),
concrete_parts.len()
));
}

let mut params = Vec::new();
for (tmpl, conc) in template_parts.iter().zip(concrete_parts.iter()) {
if tmpl.starts_with('{') && tmpl.ends_with('}') {
let name = tmpl[1..tmpl.len() - 1].to_string();
params.push((name, conc.to_string()));
} else if tmpl != conc {
return Err(format!(
"URI segment mismatch: expected '{}', got '{}'",
tmpl, conc
));
}
}

Ok(params)
}

pub fn constructor_param_names(constructor: &AgentConstructor) -> Vec<String> {
match &constructor.input_schema {
DataSchema::Tuple(NamedElementSchemas { elements }) => {
elements.iter().map(|e| e.name.clone()).collect()
}
DataSchema::Multimodal(_) => vec![],
}
}
}
Loading
Loading